EF Core中的全局查询筛选器(Global Query Filters)是一种强大的功能,可以在实体框架的DbContext级别为特定的EntityType设置默认的过滤条件。这些筛选器自动应用于所有涉及到相关实体的LINQ查询中,无论是直接查询还是通过Include或导航属性间接引用的情况。
以下是一些适用场景:
-
多租户: 在多租户应用程序中,每个租户的数据应该彼此隔离。通过使用全局查询筛选器,可以轻松确保每次查询仅返回特定租户的数据,而无需在每个查询中显式添加
Where
子句。 -
**软删除:**软删除是一种数据管理策略,允许在数据库中保留已删除的数据,而不是完全从数据库中移除。通过使用全局查询筛选器,可以自动排除那些被标记为已删除的数据行,从而在查询结果中只包含未删除的数据。
-
数据访问权限 :在某些应用中,您可能基于用户的角色或权限来限制他们可以访问的数据。全局查询筛选器可以确保即使忘记了添加权限检查,查询也仍然只返回用户可以访问的数据。
软删除
软删除是一种数据管理策略,它允许在数据库中保留已删除的数据,而不是完全从数据库中移除。这种策略通常通过添加一个布尔类型的列(如IsDeleted)来实现,该列标记着每行数据是否已被逻辑删除。
以下是一个示例
1.定义实体:首先,你需要定义包含特定属性的实体类,例如用于软删除的IsDeleted属性。
cs
public class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
}
- 配置筛选器:接下来,在OnModelCreating方法中使用HasQueryFilter API为实体类型配置查询筛选器。这些筛选器以LINQ查询谓词的形式存在,并自动应用于所有相关的查询操作。
cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>()
.HasQueryFilter(e => !e.IsDeleted);
}
- 执行删除操作:当你需要删除一个实体时,只需将IsDeleted属性设置为true,而不是实际从数据库中删除它。
cs
var entity = new MyEntity { Id = 1, Name = "Example" };
context.MyEntities.Add(entity);
await context.SaveChangesAsync();
entity.IsDeleted = true;
await context.SaveChangesAsync();
4.恢复删除的数据:如果你意外删除了数据,你可以轻易地通过更改IsDeleted标志来恢复。
cs
entity.IsDeleted = false;
await context.SaveChangesAsync();
优缺点:
优点:
- 恢复数据 :如果你意外删除了数据,你可以轻易地通过更改
IsDeleted
标志来恢复。- 审计和历史记录:保留了数据的历史记录,有助于进行审计和分析。
- 提高性能:在某些情况下,软删除可以提高性能,因为它避免了实际的数据库删除操作。
缺点:
- 维护开销:需要定期清理未使用的数据,否则可能会占用过多的存储空间。
- 查询复杂性 :每个查询都需要包含
IsDeleted
条件,除非确实需要访问已删除的数据。
多租户
多租户架构是一种将多个租户(用户或组织)共享同一套应用程序或系统的方式,同时保持每个租户的数据隔离性。这种架构模式在许多现代应用程序中被广泛使用,例如云服务提供商、SaaS应用程序等。
以下是一个示例
- 定义实体:首先,你需要定义包含特定属性的实体类,例如用于标识租户的TenantId属性。、
cs
public class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int TenantId { get; set; }
}
- 配置筛选器:接下来,在OnModelCreating方法中使用HasQueryFilter API为实体类型配置查询筛选器。这些筛选器以LINQ查询谓词的形式存在,并自动应用于所有相关的查询操作。
cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>()
.HasQueryFilter(e => e.TenantId == _currentTenantId);
}
- 执行查询操作:当你需要查询数据时,只需根据当前租户的ID进行过滤即可。
cs
// 假设当前租户的ID为1
var entities = context.MyEntities.ToList();
- 添加新记录:当你需要添加一个新记录时,只需设置TenantId属性为当前租户的ID即可。
cs
var entity = new MyEntity { Name = "Example" };
entity.TenantId = _currentTenantId;
context.MyEntities.Add(entity);
await context.SaveChangesAsync();
优缺点:
优点:
- 可定制性和灵活性:每个租户可以根据需要定制自己的数据视图和功能。
- 简化维护:可以在一个集中的位置更新和维护应用程序。
缺点:
- 数据隔离:需要小心设计以防止数据泄露或混肴。
- 性能挑战:如果所有租户都在同一时间活跃,可能会对性能造成压力。
- 备份和恢复:可能需要更复杂的备份和恢复策略来满足不同租户的需求。
数据访问权限
下面是一个示例:
假设我们有一个名为User的实体类,其中包含一个名为Role的角色字段,表示用户的角色。我们希望在执行查询时自动过滤掉不属于特定角色的用户。
首先,我们需要在DbContext的OnModelCreating方法中为User实体类型配置全局查询筛选器。
cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasQueryFilter(u => u.Role == "Admin");
}
上述代码将自动应用一个筛选条件,只返回角色为"Admin"的用户记录。
接下来,我们可以执行普通的LINQ查询来获取所有管理员用户。
cs
var adminUsers = context.Users.ToList();
由于已经配置了全局查询筛选器,上述查询将自动过滤掉非管理员用户,并返回所有管理员用户列表。
全局查询筛选器仅适用于直接查询和通过导航属性引用的情况。如果需要对关联表进行数据访问权限的查询,可以在关联表中也使用类似的全局查询筛选器。还可以根据需要自定义全局查询筛选器的表达式,以满足更复杂的业务需求。
忽略全局查询过滤器( IgnoreQueryFilters())
如果你想在执行针对Student实体类型的查询时忽略全局查询过滤器,可以使用**IgnoreQueryFilters()**方法。
例如,以下代码将忽略全局查询过滤器并返回所有Student记录:
cs
var allStudents = context.Students.IgnoreQueryFilters().ToList();
通过调用IgnoreQueryFilters()方法,你可以暂时禁用全局查询过滤器,以便执行不受其影响的查询。