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

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

原文地址:https://www.cnblogs.com/wangjieguang/p/gloadfilter.html

时间: 2024-11-11 05:14:25

在ASP.NET Core中通过EF Core实现一个简单的全局过滤查询的相关文章

EF Core 快速上手——EF Core 入门

EF Core 快速上手--EF Core 介绍 本章导航 从本书你能学到什么 对EF6.x 程序员的一些话 EF Core 概述 1.3.1 ORM框架的缺点 第一个EF Core应用 ??本文是对<Entity framework in action>部分章节的翻译,某些场景也会附上笔者实践的Demo.尽管很认真的斟酌,但是水平有限,还请各位批评和斧正. ??Entity Framework Core, 或者 EF Core,是一个方便软件工程师访问数据库的库.有很多方法来构建这样的一个库

【从0开始.NET CORE认证】-2 使用.Net Core Identity和EF Core

回顾 朋友们,距离上次从0开始.NET CORE认证-1发布已经过去一周了,上次第一篇文章,其实并没有涉及到Net Core Identity,就是简单的搭了一个项目,让大家对Identity中各种术语有个理解,明白他们出现的位置,已经他们出现能够达到某种功能.以及出现的位置顺序不同,则会出现什么不同的情况. 回顾一下上次写的主要的知识点 Authentication和Authorization 是什么,怎么解释他们 Claim和ClaimType又是什么,能举例子说明吗? ClaimsIden

关于在ASP.NET MVC 中使用EF的Code First的方式来读取数据库时的Validation failed for one or more entities. See &#39;EntityValidationErrors&#39; property for more details.

今天在做一个小网站的时候遇到很多问题唉,我还是个菜鸟,懂的也不多,今天一个表单的提交按钮用不了,都弄了几个小时唉.不过最后还是搞定了,还有浏览器有开发人员选项,不然我都不知道我还要继续排查多久哦,今天晚上在把数据存入数据库的又出现了问题.我使用的是Entity Framework的Code First模式来访问数据库的.对于数据的验证我在数据模型上加了数据注解.当然在前台也引入了一些JS这样就可以再不把数据提交到服务器时完成验证功能.在后台保存用户提交的数据的时候,我们要用到ModelStatu

用ASP.NET Core MVC 和 EF Core 构建Web应用 (一)

系统必备 .NET Core 2.0.0 SDK 或更高版本. 已安装 ASP.NET 和 Web 开发工作负载的 Visual Studio 2017 15.3 版或更高版本. 创建Web应用程序 打开 Visual Studio 并创建一个新 ASP.NET Core C# web 项目名为"ContosoUniversity". 从文件菜单上,选择新建 > 项目. 从左窗格中,选择已安装 > Visual C# > Web. 选择"ASP.NET Co

用ASP.NET Core MVC 和 EF Core 构建Web应用 (十)

之前的学习中,已经以每个类一张表的方式实现了继承. 本节将会介绍在掌握开发基础 ASP.NET Core web 应用程序之后使用 Entity Framework Core 开发时需要注意的几个问题. 原生 SQL 查询 使用 Entity Framework 的优点之一是它可避免你编写跟数据库过于耦合的代码 它会自动生成 SQL 查询和命令,使得你无需自行编写. 但有一些特殊情况,你需要执行手动创建的特定 SQL 查询. 对于这些情况下, Entity Framework Code Firs

Asp.net 在网页编写C#代码示例-- 一个简单的web MsSql 命令执行环境

在给一个客户做的系统上,因为要对数据库进行查看,但之前都是用TeamView来连接到客户的服务器进行数据库操作的 但最近客户那边的TeamView好像更改过密码导致我无法正常连接,而巧了客户的网官因为有事没有上班所以也法获取新的密码. 因为业务原因急需查看数据库,所以就写了一个简单的SQl命令并部署到客户的服务器来通过Web执行Sql命令 将ConnectonString更改为自己的数据库连接并保存为**.aspx即可 <!DOCTYPE html> <html> <head

Redis系列文章总结:ASP.Net Core 中如何借助CSRedis实现一个安全高效的分布式锁

引言:最近回头看了看之前和同事一起开发的.Net Core 2.1的项目,其中在多处用到Redis实现的分布式锁,虽然在OnResultExecuting方法中做了防止死锁的处理,但在某些场景下还是会发生死锁的问题,下面我只展示部分代码: 问题: (1)这里setnx设置的值“1”,我想问,你最后del的这个值一定是你自己创建的吗? (2)图中标注的步骤1和步骤2不是原子操作,会有死锁的概率吗? 大家可以思考一下先,下面让我们带着这两个问题往下看,下面介绍一下使用Redis实现分布式锁常用的几个

.net core 中使用ef 访问mysql

1.参考文档说修改项目文件添加,就得这么做,不然会报错 <ItemGroup> <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" /> </ItemGroup> https://www.cnblogs.com/Starts_2000/p/mysql-efcore20-codefirst-dbfir

ASP.NET MVC中使用EF框架

在公司中一直在使用oracle数据库,然后天天都是手写T-SQL,用的要么是ado.net封装好的操作数据库的方法,要么用的是oracle原生的一些操作数据库的方法,以前也自己了解过一些微软ORM的东西,更是听说过Entity Framework的大名,今天手动操作了一下, 想起了webcast里苏鹏说的那句话,微软把东西做的这么简单,基本不用谢什么代码,按几个按钮或者配置几下就生成了一堆东西,这究竟是帮助了程序员还是害了程序员.感叹没用,还是记录下动手的一些过程. 首先新建了一个mvc4的应用