zoukankan      html  css  js  c++  java
  • 在ASP.NET Core中通过EF Core实现一个简单的全局过滤查询

    前言

    不知道大家是否和我有同样的问题:

    一般在数据库的设计阶段,会制定一些默认的规则,其中有一条硬性规定就是一定不要对任何表中的数据执行delete硬删除操作,因为每条数据对我们来说都是有用的,并且是值得分析的。

    所以我们一般会在每张表中加一个“是否删除IsDeleted”或者“是否有效IsValid”的字段,来标识这条数据的状态是否可用!

    那么疑问来了,在写SQL或者Linq的时候我们到底是要加上这个条件还是忽略这个条件呢?答案当然是根据实际业务需求和情况来决定。比如一个商品,在货架上的时候,它肯定是有效的并且是供顾客进行选购的;但是有一天被通知下架了(删除了),那么在顾客的已订单列表中你也同样要显示出来供顾客查看!

    不过话说回来,我觉得大多时候查询的时候我们都会将这些无效的数据给过滤掉,所以每个SQL或者Linq中都有随处可见的IsDeleted=0类似这样的条件,而且有时候我们还会一不小心就把这个条件忘记在了脑后。那么有没有一种一劳永逸的或者更加便捷的方法来解决这个问题呢?这时主角EF Core就上场了!

    1、使用EF Core自带的全局过滤查询功能

    这个使用非常之简单,只需要在OnModelCreating中对需要进行全局过滤的表实体中设置ModelBuilder就可以了。先在系统用户表里边准备一条删除和未删除的数据。

        /// <summary>
        /// 系统上下文
        /// </summary>
        public class LightContext : DbContext
        {
            public LightContext(DbContextOptions<LightContext> options) : base(options)
            {
            }
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.Entity<ApplicationUser>(m =>
                {
                    m.Property(t => t.Email)
                            .HasMaxLength(50);
    
                    m.Property(t => t.UserName)
                            .IsRequired()
                            .HasMaxLength(50);
    
                    m.Property(t => t.Password)
                            .IsRequired()
                            .HasMaxLength(20);
    
                    m.HasQueryFilter(n => !n.IsDeleted); //默认查询未删除的用户
                });
                base.OnModelCreating(modelBuilder);
            }
    
            /// <summary>
            /// 系统应用用户
            /// </summary>
            public DbSet<ApplicationUser> ApplicationUser { get; set; }
        }

     运行程序然后请求用户接口,那么结果就是我们只获取到Id=1的未删除数据,Id=2的数据已经达到了我们的预期被过滤掉了!

    分析:上面的做法虽然达到了效果,但是还不够灵活。如果这时我要查询所有的数据,那么这个全局过滤条件还得删掉,还有就是如果我要偏偏查询已删除的数据呢,又得改代码了!

    所以,我们每次查询的时候都需要接受一个条件,来标识所查询数据的有效性,并将这个条件参数传递给数据库上下文DbContext,动态的去获取我们想要的数据!

    2、在ASP.NET Core中接受全局过滤参数

     首先第一步我们要在服务配置项中借助请求上下文HttpContext来动态接受一个“是否删除”的参数对象,我们暂时将这个参数定义为“d”,含义分别为:0:未删除,1:已删除,2:全部,同样默认查询所有未删除的数据

     然后将这个参数以数据库上下文DbContext的构造函数传递进去,同时要考虑到get请求和post请求,最终的代码如下:

            public void ConfigureServices(IServiceCollection services)
            {
                // Add DbContext
                //services.AddDbContext<LightContext>(options =>
                //    options.UseSqlServer(Configuration.GetConnectionString("LightConnection")));
    
                services.AddTransient<LightContext>(factory =>
                {
                    var builder = new DbContextOptionsBuilder<LightContext>();
                    builder.UseSqlServer(Configuration.GetConnectionString("LightConnection"));
    
                    var accessor = factory.GetService<IHttpContextAccessor>();
                    bool? isDeleted = false;//默认全局查询未删除的数据
    
                    if (accessor.HttpContext != null)
                    {
                        string method = accessor.HttpContext.Request.Method.ToLower();
                        StringValues queryIsdeleted = StringValues.Empty;
                        if (method == "get")
                        {
                            queryIsdeleted = accessor.HttpContext.Request.Query["d"];
                        }
                        else if (method == "post" && accessor.HttpContext.Request.HasFormContentType)
                        {
                            queryIsdeleted = accessor.HttpContext.Request.Form["d"];
                        }
                        if (!StringValues.IsNullOrEmpty(queryIsdeleted))
                        {
                            int isDeletedInt = 0;//0:未删除,1:已删除,2:全部
                            if (int.TryParse(queryIsdeleted.FirstOrDefault(), out isDeletedInt))
                            {
                                if (isDeletedInt == 0)
                                {
                                    isDeleted = false;
                                }
                                else if (isDeletedInt == 1)
                                {
                                    isDeleted = true;
                                }
                                else if (isDeletedInt == 2)
                                {
                                    isDeleted = null;
                                }
                            }
                        }
                    }
                    return new LightContext(builder.Options, isDeleted);
                });
            }

    3、在EF Core仓储中添加自定义过滤条件

     接下来在数据库上下文DbContext增加一个IsDeleted的查询条件并私有化赋值操作,仅仅交由构造函数进行赋值。改动的代码如下:

        /// <summary>
        /// 系统上下文
        /// </summary>
        public class LightContext : DbContext
        {
            public bool? IsDeleted { get; private set; } //禁止外界对IsDeleted进行赋值操作,限制在构造函数赋值
    
            public LightContext(DbContextOptions<LightContext> options, bool? isDeleted = false) : base(options)
            {
                IsDeleted = isDeleted;
            }
        }

     然后这个条件就可以在我们的EF仓储模块进行使用了,根据我们的实际需求可以进行不同的条件查询,部分代码如下:

        /// <summary>
        /// EF 实现仓储接口
        /// </summary>
        /// <typeparam name="T">实体</typeparam>
        public class EfRepository<T> : IRepository<T>, IRepositoryAsync<T> where T : BaseModel
        {
            protected readonly LightContext _lightContext;
    
            public EfRepository(LightContext lightContext)
            {
                _lightContext = lightContext;
            }
    
            public T GetById(int id)
            {
                if (_lightContext.IsDeleted.HasValue)
                {
                    return _lightContext.Set<T>().Where(m => m.IsDeleted == _lightContext.IsDeleted.Value).FirstOrDefault(m => m.Id == id);
                }
                return _lightContext.Set<T>().FirstOrDefault(m => m.Id == id);
            }
    
            public async Task<T> GetByIdAsync(int id)
            {
                if (_lightContext.IsDeleted.HasValue)
                {
                    return await _lightContext.Set<T>().Where(m => m.IsDeleted == _lightContext.IsDeleted.Value).FirstOrDefaultAsync(m => m.Id == id);
                }
                return await _lightContext.Set<T>().FirstOrDefaultAsync(m => m.Id == id);
            }
    
            public IEnumerable<T> GetList()
            {
                if (_lightContext.IsDeleted.HasValue)
                {
                    return _lightContext.Set<T>().Where(m => m.IsDeleted == _lightContext.IsDeleted.Value).ToList();
                }
                return _lightContext.Set<T>().ToList();
            }
    
            public async Task<IEnumerable<T>> GetListAsync()
            {
                if (_lightContext.IsDeleted.HasValue)
                {
                    return await _lightContext.Set<T>().Where(m => m.IsDeleted == _lightContext.IsDeleted.Value).ToListAsync();
                }
                return await _lightContext.Set<T>().ToListAsync();
            }
        }

    最后EF Core自带的那个过滤查询就可以完全忽略掉了:

    //m.HasQueryFilter(n => !n.IsDeleted); //默认查询未删除的用户

    至此整个调整已经完成,虽然看似简单,但是感觉还挺实用的,同样如果需要其他通用的过滤条件,比如时间之类的,都可以酌情添加!最终的效果如下:

    4、最后

    每天进步一点点,是件很愉快的事情!提前预祝大家新年快乐:)

  • 相关阅读:
    my first blog
    CentOS启动报错:Centos kernel panic-not syncing:VFS:Unable to mount root fs on unknown block
    J2EE在Web容器中启动报错:com.sun.jdi.InvocationException occurred invoking method.
    SVN报错:can't open file db/txn-current-lock:permission denied 解决方法
    Linux(CentOS6.4、CentOS6.3)下安装、配置PostgreSQL9.2
    [模板] 线性代数:矩阵/高斯消元/矩阵求逆/行列式
    [模板] 杂项:二分/离散化/随机数/其他
    [模板] 图论:存储/拓扑排序
    Luogu1137 旅行计划(拓扑排序)
    C++模板、.vimrc和一些Linux配置
  • 原文地址:https://www.cnblogs.com/wangjieguang/p/gloadfilter.html
Copyright © 2011-2022 走看看