在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、最后

每天進步一點點,是件很愉快的事情!提前預祝大家新年快樂:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM