EF Core 中实现 动态数据过滤器

Posted wulaiwei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EF Core 中实现 动态数据过滤器相关的知识,希望对你有一定的参考价值。

 

前言

  在项目开发中,我们很多时候都会设计  软删除、所属用户 等等一系列字段 来方便我们在业务查询的时候进行各种过滤

  然后引申的问题就是:

    在业务查询的时候,我们要如何加上这些条件?或者动态禁用某些查询条件呢?

EF Core自带的全局过滤查询功能

  EF Core提供了一个HasQueryFilter 供我们在查询的时候进行预置部分筛选条件 

  例如:

      builder.HasQueryFilter(x => !x.IsDelete);

  这样查询的时候  EF Core 会自动帮我们实现过滤

  然后如果不想使用的时候可以全部忽略      

    DbSet.IgnoreQueryFilters();

  咋一看 很完美  

  然后我们实际操作的时候

  1.我是不是每个Entity里面是不是都要配置一次呢?

  2.我只想禁用部分筛选条件呢?

  3.我的查询条件的某些参数要动态呢?

    例如和用户相关的数据等等

    (有些人可能会说 我想办法把User的信息注入到DbContext里面不就可以了  假如我还要别的信息呢  还是接着注入?)

  这就是理论和实践之间的差距

  然后再网上找好久,找到了 EntityFramework-Plus  (开源免费)

  https://github.com/zzzprojects/EntityFramework-Plus

     官网地址: http://entityframework-plus.net/  

  内置了很多功能  本篇只针对查询过滤做说嘛

 

 EntityFramework-Plus 查询过滤功能

  1.QueryFilterManager

  QueryFilterManager 主要用来预设全局过滤

  例如:

  QueryFilterManager.Filter<Customer>(q => q.Where(x => x.IsActive));

  var ctx = new EntitiesContext();

  QueryFilterManager.InitilizeGlobalFilter(ctx);

  这样即可。。。

  但是需要提前注意的是 QueryFilterManager 预设后是无法更改的

  无法更改这是在  谷歌的时候  作者正好回复的别人的时候看到的

  就和我们之前第三点  动态 冲突了

  然后只能再看别的方式了

  2.Filter

  Z.EntityFramework.Plus 提供了 通过DbContext 的扩展方式来进行注入筛选条件的方式

  例如:

  var ctx = new EntitiesContext();

  ctx.Filter<IAnimal>(MyEnum.EnumValue, q => q.Where(x => x.IsDomestic))

  //禁用指定键值查询条件

  ctx.Filter(MyEnum.EnumValue).Disable();

  var dogs = ctx.Dogs.ToList();

  //启用指定键值查询条件

  ctx.Filter(MyEnum.EnumValue).Enable();

  // SELECT * FROM Dog WHERE IsDomestic = true
  var dogs = ctx.Dogs.ToList();

  这样好像符合我们的需求  

  3.AsNoFilter

  禁用条件

  例如: 

  var ctx = new EntitiesContext();

  this.Filter<Customer>(q => q.Where(x => x.IsActive));

  // SELECT * FROM Customer WHERE IsActive = true
  var list = ctx.Customers.ToList();

  // SELECT * FROM Customer
  var list = ctx.Customers.AsNoFilter().ToList();

 

  AsNoFilter()后如何启用  指定查询条件  作者好像没有做相应扩展 ,后面会给出对应扩展方法

-----------------------------------------------------------------------------------------------------------------------------------------------------------

说了这么多 理论补完了   实际操作的时候呢?

  1.这些条件如何注入进来呢?

  2.如何可以让我任意扩展呢?

     3.假如我们操作时通过仓储 ,而不是  直接通过DbContext 呢?

 

 

如何封装

 这边演示通过我自己的开源项目做为事例:

  github  : https://github.com/wulaiwei/WorkData.Core

  主要依赖的框架

  1.AutoFac

  2.EF Core

  3.Z.EntityFramework.Plus

-----------------------------------------------------------------------------------------------------------------------------------------  

  对于我们来说 我们无论使用多少个数据筛选器  返回的都应该是同一个返回值  ,我们去看 DbContext.Filter(....)  会发现他的返回值都是 BaseQueryFilter

  针对这个  我们可以得到两条信息  我们需要 传入 DbContext  和 一个返回值为  BaseQueryFilter  的方法

  所以 我们定义如下接口 IDynamicFilter

技术分享图片
1     public interface IDynamicFilter
2     {
3         BaseQueryFilter InitFilter(DbContext dbContext);
4     }
View Code

  这样我们这边就得到了一个标准

  例如 我们我们需要一个 所属用户和  软删除 的数据筛选器   我们只需要继承他即可

  我们如何区分他们呢?

  我们在之前使用 Z.EntityFramework.Plus  是看到了  可以设置筛选器的Key

  所以 我们也同样扩展个属性 DynamicFilterAttribute  来作为他们的名字

技术分享图片
1     public class DynamicFilterAttribute: Attribute
2     {
3         /// <summary>
4         /// Name
5         /// </summary>
6         public string Name { get; set; }
7 
8     }
View Code

  然后我们定义我们的  所属用户和  软删除 的数据筛选器  并为他们设置名称

  CreateDynamicFilter

技术分享图片
 1  /// <summary>
 2     /// CreateDynamicFilter
 3     /// </summary>
 4     [DynamicFilter(Name = "CreateUserId")]
 5     public class CreateDynamicFilter : IDynamicFilter
 6     {
 7         /// <summary>
 8         /// InitFilter
 9         /// </summary>
10         /// <param name="dbContext"></param>
11         /// <returns></returns>
12         public BaseQueryFilter InitFilter(DbContext dbContext)
13         {
14             var workdataSession = IocManager.Instance.Resolve<IWorkDataSession>();
15             if (workdataSession == null)
16                 return dbContext
17                     .Filter<ICreate>("CreateUserId", x => x.Where(w => w.CreateUserId == string.Empty ));
18 
19             return dbContext
20                 .Filter<ICreate>("CreateUserId", x => x.Where(w => w.CreateUserId == workdataSession.UserId || w.CreateUserId == ""));
21         }
22     }
View Code

  说明:

  var workdataSession = IocManager.Instance.Resolve<IWorkDataSession>();

  用来获取你所需要的 传参 

     IocManager.Instance.Resolve  是WorkData  关于Ioc的封装  源码可以参见git  或者上一篇博客

  SoftDeleteDynamicFilter

技术分享图片
 1 /// <summary>
 2     /// SoftDeleteDynamicFilter
 3     /// </summary>
 4     [DynamicFilter(Name = "SoftDelete")]
 5     public class SoftDeleteDynamicFilter: IDynamicFilter
 6     {
 7         public BaseQueryFilter InitFilter(DbContext dbContext)
 8         {
 9             return dbContext
10                 .Filter<IsSoftDelete>("SoftDelete", x => x.Where(w => !w.IsDelete));
11         }
12     }
View Code

   这样 我们所有接口 和实现定义好了 如何管理呢?

  1.将继承 IDynamicFilter 的注入到Ioc里面

技术分享图片
 1             #region 动态审计注入
 2             var filterTypes = _typeFinder.FindClassesOfType<IDynamicFilter>();
 3 
 4             foreach (var filterType in filterTypes)
 5             {
 6                 var dynamicFilterAttribute = filterType.GetCustomAttribute(typeof(DynamicFilterAttribute)) as DynamicFilterAttribute;
 7                 if (dynamicFilterAttribute == null)
 8                     continue;
 9 
10                 builder.RegisterType(filterType).Named<IDynamicFilter>(dynamicFilterAttribute.Name);
11             }
12             #endregion
View Code

  说明:

  1.ITypeFinder 是从 nopcommerce 抽离出来的反射方法   已集成到WorkData  百度即可查询到相应说明文档

  2.通过 GetCustomAttribute  获取 DynamicFilterAttribute 的属性名称  作为注册到Ioc名称

  2.如何设置一个启用数据筛选器呢?我们这边定义个配置文件  通过 .net core 提供的程序进行配置文件注入

技术分享图片
1     /// <summary>
2     /// 动态拦截器配置
3     /// </summary>
4     public class DynamicFilterConfig
5     {
6         public List<string> DynamicFilterList{ get; set; }
7     }
View Code

"DynamicFilterConfig": {

"DynamicFilterList": [ "CreateUserId", "SoftDelete" ]
}

 

如何注入配置文件 可以通过百度或者查看workdata源码 即可 这不做说明

  3.如何管理呢?什么时候统一添加到 DbContext呢?

  我们这边定义一个DynamicFilterManager 提供一个 字典集合 来暂存所以的 IDynamicFilter,同时提供一个方法来进行初始化值

技术分享图片
 1     public static class DynamicFilterManager
 2     {
 3         static DynamicFilterManager()
 4         {
 5             CacheGenericDynamicFilter = new Dictionary<string, IDynamicFilter>();
 6         }
 7 
 8         /// <summary>
 9         ///     CacheGenericDynamicFilter
10         /// </summary>
11         public static Dictionary<string, IDynamicFilter> CacheGenericDynamicFilter { get; set; }
12 
13         /// <summary>
14         ///     AddDynamicFilter
15         /// </summary>
16         /// <param name="dbContext"></param>
17         /// <returns></returns>
18         public static void AddDynamicFilter(this DbContext dbContext)
19         {
20             if (dbContext == null) return;
21             foreach (var dynamicFilter in CacheGenericDynamicFilter) dynamicFilter.Value.InitFilter(dbContext);
22         }
23 
24         /// <summary>
25         ///     AsWorkDataNoFilter
26         /// </summary>
27         /// <typeparam name="T"></typeparam>
28         /// <param name="query"></param>
29         /// <param name="context"></param>
30         /// <param name="filterStrings"></param>
31         /// <returns></returns>
32         public static IQueryable<T> AsWorkDataNoFilter<T>(this DbSet<T> query, DbContext context,
33             params object[] filterStrings) where T : class
34         {
35             var asNoFilterQueryable = query.AsNoFilter();
36 
37             object query1 = asNoFilterQueryable;
38             var items = CacheGenericDynamicFilter.Where(x => filterStrings.Contains(x.Key));
39 
40             query1 = items.Select(key => context.Filter(key.Key)).Where(item => item != null)
41                 .Aggregate(query1, (current, item) => (IQueryable) item.ApplyFilter<T>(current));
42             return (IQueryable<T>) query1;
43         }
44 
45         /// <summary>
46         ///     SetCacheGenericDynamicFilter
47         /// </summary>
48         public static void SetCacheGenericDynamicFilter()
49         {
50             var dynamicFilterConfig = IocManager.Instance.ResolveServiceValue<DynamicFilterConfig>();
51 
52             foreach (var item in dynamicFilterConfig.DynamicFilterList)
53             {
54                 var dynamicFilter = IocManager.Instance.ResolveName<IDynamicFilter>(item);
55                 CacheGenericDynamicFilter.Add(item, dynamicFilter);
56             }
57         }
58     }
View Code

  然后我们在DbContext里面的 OnModelCreating 进行初始化

技术分享图片
 1  /// <summary>
 2         ///     重写模型创建函数
 3         /// </summary>
 4         /// <param name="modelBuilder"></param>
 5         protected override void OnModelCreating(ModelBuilder modelBuilder)
 6         {
 7             base.OnModelCreating(modelBuilder);
 8 
 9             //初始化对象
10             DynamicFilterManager.SetCacheGenericDynamicFilter();
11         }
View Code

  初始化完成后如何将条件付给  DbContext 呢?

  在DynamicFilterManager 中我们提供了一个扩展方法 AddDynamicFilter 你可以在你创建 DbContext 的时候调用

技术分享图片
 1      /// <summary>
 2         ///     AddDynamicFilter
 3         /// </summary>
 4         /// <param name="dbContext"></param>
 5         /// <returns></returns>
 6         public static void AddDynamicFilter(this DbContext dbContext)
 7         {
 8             if (dbContext == null) return;
 9             foreach (var dynamicFilter in CacheGenericDynamicFilter) dynamicFilter.Value.InitFilter(dbContext);
10         }
View Code

  在WorkData中  我们则需要在EfContextFactory 进行调用

  dbContext = _resolver.Resolve<TDbContext>();

  //初始化拦截器
  dbContext.AddDynamicFilter();

技术分享图片
 1   /// <summary>
 2     ///     EfContextFactory
 3     /// </summary>
 4     public class EfContextFactory : IEfContextFactory
 5     {
 6         private readonly IResolver _resolver;
 7 
 8         public EfContextFactory(IResolver resolver)
 9         {
10             _resolver = resolver;
11         }
12 
13         /// <summary>
14         ///     default current context
15         /// </summary>
16         /// <param name="dic"></param>
17         /// <param name="tranDic"></param>
18         /// <returns></returns>
19         public TDbContext GetCurrentDbContext<TDbContext>(Dictionary<string, DbContext> dic, Dictionary<DbContext, IDbContextTransaction> tranDic)
20             where TDbContext : DbContext
21         {
22             return GetCurrentDbContext<TDbContext>(dic, tranDic, string.Empty);
23         }
24 
25         /// <summary>
26         ///GetCurrentDbContext
27         /// </summary>
28         /// <typeparam name="TDbContext"></typeparam>
29         /// <param name="dic"></param>
30         /// <param name="tranDic"></param>
31         /// <param name="conString"></param>
32         /// <returns></returns>
33         public TDbContext GetCurrentDbContext<TDbContext>(Dictionary<string, DbContext> dic, Dictionary<DbContext, IDbContextTransaction> tranDic, string conString)
34             where TDbContext : DbContext
35         {
36             conString = typeof(TDbContext).ToString();
37             var dbContext = dic.ContainsKey(conString + "DbContext") ? dic[conString + "DbContext"] : null;
38             try
39             {
40                 if (dbContext != null)
41                 {
42                     return (TDbContext)dbContext;
43                 }
44             }
45             catch (Exception)
46             {
47                 dic.Remove(conString + "DbContext");
48             }
49             dbContext = _resolver.Resolve<TDbContext>();
50 
51             //初始化拦截器
52             dbContext.AddDynamicFilter();
53 
54             //我们在创建一个,放到数据槽中去
55             dic.Add(conString + "DbContext", dbContext);
56 
57             //开始事务
58             var tran = dbContext.Database.BeginTransaction();
59             tranDic.Add(dbContext, tran);
60 
61             return (TDbContext)dbContext;
62         }
63     }
View Code

  这样我们的筛选器已经全部注入完成了

  还剩下一个我们之前说的  

  AsNoFilter()后如何启用  指定查询条件  作者好像没有做相应扩展 ,后面会给出对应扩展方法

  通过查看源码后 

技术分享图片
 1     /// <summary>
 2     ///     Filter the query using context filters associated with specified keys.
 3     /// </summary>
 4     /// <typeparam name="T">The type of elements of the query.</typeparam>
 5     /// <param name="query">The query to filter using context filters associated with specified keys.</param>
 6     /// <param name="keys">
 7     ///     A variable-length parameters list containing keys associated to context filters to use to filter the
 8     ///     query.
 9     /// </param>
10     /// <returns>The query filtered using context filters associated with specified keys.</returns>
11     public static IQueryable<T> Filter<T>(this DbSet<T> query, params object[] keys) where T : class
12     {
13       BaseQueryFilterQueryable filterQueryable = QueryFilterManager.GetFilterQueryable((IQueryable) query);
14       IQueryable<T> query1 = filterQueryable != null ? (IQueryable<T>) filterQueryable.OriginalQuery : (IQueryable<T>) query;
15       return QueryFilterManager.AddOrGetFilterContext(filterQueryable != null ? filterQueryable.Context : InternalExtensions.GetDbContext<T>(query)).ApplyFilter<T>(query1, keys);
16     }
View Code

  Z.EntityFramework.Plus  提供了一个 ApplyFilter  所以 我们基于这个 做个扩展

技术分享图片
 1   /// <summary>
 2         ///     AsWorkDataNoFilter
 3         /// </summary>
 4         /// <typeparam name="T"></typeparam>
 5         /// <param name="query"></param>
 6         /// <param name="context"></param>
 7         /// <param name="filterStrings"></param>
 8         /// <returns></returns>
 9         public static IQueryable<T> AsWorkDataNoFilter<T>(this DbSet<T> query, DbContext context,
10             params object[] filterStrings) where T : class
11         {
12             var asNoFilterQueryable = query.AsNoFilter();
13 
14             object query1 = asNoFilterQueryable;
15             var items = CacheGenericDynamicFilter.Where(x => filterStrings.Contains(x.Key));
16 
17             query1 = items.Select(key => context.Filter(key.Key)).Where(item => item != null)
18                 .Aggregate(query1, (current, item) => (IQueryable) item.ApplyFilter<T>(current));
19             return (IQueryable<T>) query1;
20         }
View Code

这样 我们可以传入指定的筛选器名称  启用自己想要的

最终我们的仓储就变成了这样:

技术分享图片
  1  /// <summary>
  2     ///     EfBaseRepository
  3     /// </summary>
  4     /// <typeparam name="TEntity"></typeparam>
  5     /// <typeparam name="TPrimaryKey"></typeparam>
  6     /// <typeparam name="TDbContext"></typeparam>
  7     public class EfBaseRepository<TDbContext, TEntity, TPrimaryKey> :
  8         BaseRepository<TEntity, TPrimaryKey>,
  9         IRepositoryDbConntext where TEntity : class, IAggregateRoot, IEntity<TPrimaryKey>
 10         where TDbContext : DbContext
 11     {
 12         //public IQueryable<EntityType> EntityTypes => Context.Model.EntityTypes.Where(t => t.Something == true);
 13 
 14         private readonly IDbContextProvider<TDbContext> _dbContextProvider;
 15         private readonly IPredicateGroup<TEntity> _predicateGroup;
 16 
 17         public EfBaseRepository(
 18             IDbContextProvider<TDbContext> dbContextProvider,
 19             IPredicateGroup<TEntity> predicateGroup)
 20         {
 21             _dbContextProvider = dbContextProvider;
 22             _predicateGroup = predicateGroup;
 23         }
 24 
 25         /// <summary>
 26         ///     Gets EF DbContext object.
 27         /// </summary>
 28         public TDbContext Context => _dbContextProvider.GetContent();
 29 
 30         /// <summary>
 31         ///     Gets DbSet for given entity.
 32         /// </summary>
 33         public virtual DbSet<TEntity> DbSet => Context.Set<TEntity>();
 34 
 35         #region DbContext
 36 
 37         /// <summary>
 38         ///     GetDbContext
 39         /// </summary>
 40         /// <returns></returns>
 41         public DbContext GetDbContext()
 42         {
 43             return Context;
 44         }
 45 
 46         #endregion
 47 
 48         #region Query
 49 
 50 
 51 
 52         /// <summary>
 53         ///     FindBy
 54         /// </summary>
 55         /// <param name="primaryKey"></param>
 56         /// <returns></returns>
 57         public override TEntity FindBy(TPrimaryKey primaryKey)
 58         {
 59             var entity = DbSet.Find(primaryKey);
 60             return entity;
 61         }
 62 
 63         /// <summary>
 64         /// FindBy
 65         /// </summary>
 66         /// <param name="primaryKey"></param>
 67         /// <param name="includeNames"></param>
 68         /// <returns></returns>
 69         public override TEntity FindBy(TPrimaryKey primaryKey, string[] includeNames)
 70         {
 71             var query = DbSet;
 72             foreach (var includeName in includeNames)
 73             {
 74                 query.Include(includeName);
 75             }
 76             var entity = query.Find(primaryKey);
 77             return entity;
 78         }
 79 
 80         /// <summary>
 81         ///     AsNoFilterFindBy
 82         /// </summary>
 83         /// <param name="primaryKey"></param>
 84         /// <returns></returns>
 85         public override TEntity AsNoFilterFindBy(TPrimaryKey primaryKey)
 86         {
 87             var entity = DbSet.AsNoFilter()
 88                 .SingleOrDefault(x => x.Id.Equals(primaryKey));
 89             return entity;
 90         }
 91 
 92         /// <summary>
 93         /// AsNoFilterFindBy
 94         /// </summary>
 95         /// <param name="primaryKey"></param>
 96         /// <param name="includeNames"></param>
 97         /// <returns></returns>
 98         public override TEntity AsNoFilterFindBy(TPrimaryKey primaryKey, string[] includeNames)
 99         {
100 
101             var query = DbSet.AsNoFilter();
102             foreach (var includeName in includeNames)
103             {
104                 query.Include(includeName);
105             }
106             var entity = query.SingleOrDefault(x => x.Id.Equals(primaryKey));
107 
108             return entity;
109         }
110 
111 
112         /// <summary>
113         ///     FindBy
114         /// </summary>
115         /// <param name="primaryKey"></param>
116         /// <param name="filterStrings"></param>
117         /// <returns></returns>
118         public override TEntity FindBy(TPrimaryKey primaryKey, params object[] filterStrings)
119         {
120             var entity = DbSet.AsWorkDataNoFilter(Context, filterStrings)
121                 .SingleOrDefault(x => x.Id.Equals(primaryKey));
122             return entity;
123         }
124 
125         /// <summary>
126         /// FindBy
127         /// </summary>
128         /// <param name="primaryKey"></param>
129         /// <param name="includeNames"></param>
130         /// <param name="filterStrings"></param>
131         /// <returns></returns>
132         public override TEntity FindBy(TPrimaryKey primaryKey, string[] includeNames, params object[] filterStrings)
133         {
134             var query = DbSet.AsWorkDataNoFilter(Context, filterStrings);
135             foreach (var includeName in includeNames)
136             {
137                 query.Include(includeName);
138             }
139             var entity = query.SingleOrDefault(x => x.Id.Equals(primaryKey));
140 
141             return entity;
142         }
143 
144 
145         /// <summary>
146         ///     GetAll
147         /// </summary>
148         /// <returns></returns>
149         public override IQueryable<TEntity> GetAll()
150         {
151             return DbSet;
152         }
153 
154 
155         /// <summary>
156         /// GetAll
157         /// </summary>
158         /// <param name="includeNames"></param>
159         /// <returns></returns>
160         public override IQueryable<TEntity> GetAll(string[] includeNames)
161         {
162             var query = DbSet;
163             foreach (var includeName in includeNames)
164             {
165                 query.Include(includeName);
166             }
167             return query;
168         }
169 
170         /// <summary>
171         /// GetAll
172         /// </summary>
173         /// <param name="filterStrings"></param>
174         /// <returns></returns>
175         public override IQueryable<TEntity> GetAll(params object[] filterStrings)
176         {
177             return DbSet.AsWorkDataNoFilter(Context, filterStrings);
178         }
179 
180         /// <summary>
181         /// GetAll
182         /// </summary>
183         /// <param name="includeNames"></param>
184         /// <param name="filterStrings"></param>
185         /// <returns></returns>
186         public override IQueryable<TEntity> GetAll(string[] includeNames, params object[] filterStrings)
187         {
188             var query = DbSet.AsWorkDataNoFilter(Context, filterStrings);
189 
190             foreach (var includeName in includeNames)
191             {
192                 query.Include(includeName);
193             }
194             return query;
195         }
196 
197         /// <summary>
198         /// AsNoFilterGetAll
199         /// </summary>
200         /// <returns></returns>
201         public override IQueryable<TEntity> AsNoFilterGetAll()
202         {
203             return DbSet.AsNoFilter();
204         }
205 
206         /// <summary>
207         /// AsNoFilterGetAll
208         /// </summary>
209         /// <param name="includeNames"></param>
210         /// <returns></returns>
211         public override IQueryable<TEntity> AsNoFilterGetAll(string[] includeNames)
212         {
213             var query = DbSet.AsNoFilter();
214 
215             foreach (var includeName in includeNames)
216             {
217                 query.Include(includeName);
218             }
219             return query;
220         }
221         #endregion
222 
223         #region Insert
224 
225         /// <summary>
226         ///     Insert
227         /// </summary>
228         /// <typeparam name="TEntity"></typeparam>
229         /// <param name="model"></param>
230         public override TEntity Insert(TEntity model)
231         {
232             return DbSet.Add(model).Entity;
233         }
234 
235         /// <summary>
236         ///     InsertGetId
237         /// </summary>
238         /// <param name="model"></param>
239         /// <returns></returns>
240         public override TPrimaryKey InsertGetId(TEntity model)
241         {
242             model = Insert(model);
243 
244             Context.SaveChanges();
245 
246             return model.Id;
247         }
248 
249         /// <summary>
250         ///     Insert
251         /// </summary>
252         /// <param name="entities"></param>
253         public override void Insert(IEnumerable<TEntity> entities)
254         {
255             if (entities == null)
256                 throw new ArgumentNullException(nameof(entities));
257 
258             DbSet.AddRange(entities);
259 
260             Context.SaveChanges();
261         }
262 
263         #endregion
264 
265         #region Delete
266 
267         /// <summary>
268         ///     Delete
269         /// </summary>
270         /// <param name="entity"></param>
271         public override void Delete(TEntity entity)
272         {
273             DbSet.Remove(entity);
274             Context.SaveChanges();
275         }
276 
277         /// <summary>
278         ///     Delete
279         /// </summary>
280         /// <param name="entities"></param>
281         public override void Delete(IEnumerable<TEntity> entities)
282         {
283             if (entities == null)
284                 throw new ArgumentNullException(nameof(entities));
285 
286             DbSet.RemoveRange(entities);
287 
288             Context.SaveChanges();
289         }
290 
291         #endregion
292 
293         #region Update
294 
295         /// <summary>
296         ///     Update
297         /// </summary>
298         /// <param name="entity"></param>
299         public override void Update(TEntity entity)
300         {
301             DbSet.Update(entity);
302             Context.SaveChanges();
303         }
304 
305         /// <summary>
306         ///     Update
307         /// </summary>
308         /// <param name="entities"></param>
309         public override void Update(IEnumerable<TEntity> entities)
310         {
311             if (entities == null)
312                 throw new ArgumentNullException(nameof(entities));
313 
314             DbSet.UpdateRange(entities);
315 
316             Context.SaveChanges();
317         }
318 
319         #endregion
320     }
View Code

  说明:仓储的设计理念是从  ABP中抽离出来的 

  最后附测试  

启用的筛选器为 "CreateUserId", "SoftDelete"

技术分享图片
 1  /// <summary>
 2         ///     Index
 3         /// </summary>
 4         /// <returns></returns>
 5         public IActionResult Index()
 6         {
 7             _baseRepository.GetAll().ToList();
 8             _baseRepository.GetAll("CreateUserId","xxx假定不存在的筛选器").ToList();
 9             _baseRepository.AsNoFilterGetAll().ToList();
10 
11             _baseRepository.FindBy("1");
12             _baseRepository.FindBy("1", "CreateUserId", "xxx假定不存在的筛选器");
13             _baseRepository.AsNoFilterFindBy("1");
14             return View();
15         }
View Code

技术分享图片

以上是关于EF Core 中实现 动态数据过滤器的主要内容,如果未能解决你的问题,请参考以下文章

EF Core 数据过滤

如何在 .NET EF Core 中实现自引用多对多关系

在 EF Core 7 中实现强类型 ID

ef core实现软删除

如何将领域类与EF代码第一层分离并在项目中实现DDD

如何在使用 TPT 层次结构时首先在 EF 代码中实现并发