• EF联合查询,如何设置条件过滤从表数据


    最近在使用EF进行联合查询过程中,遇到了一件不开心的事情。

    已禁用懒加载

    var post = await _repository.GetMyPostById(blogId, postId).AsNoTracking()
                            .Include(p => p.PostToCategories)
                            .SingleOrDefaultAsync();
    
    如上代码所示的查询中,使用Include()关联了PostToCategories,这是常用的联合查询方式。可是PostToCategories是软删除(IsAcitve)的,使用Include()方法会把所有的相关的PostToCategories都查询出来,这不是我们想要的结果。
    首先分析下原因,Include()方法,是根据配置的关系查询关联的对象,所以我们只要在它生成sql之前加上过滤条件就可以了,可是纵观EF的api,无一能实现目的。既然这样,我们只能想想其他办法了。
    
    var post = await _repository.GetMyPostById(blogId, postId).AsNoTracking()
                            .Select( p => new {
                                                    BlogPost = p,
                                                    PostToCategories = p.PostToCategories.Where(c => c.IsActive)
                                                    }).SingleOrDefaultAsync();
    var blogPost = post.BlogPost;
    blogPost.PostToCategories = post.PostToCategories.ToList();
    
    
    用匿名类型接受主表和从表的查询结果,这样就可以为从表设置过滤条件了。最后,把匿名结果显示赋值给blogPost。
    通过观察生成的sql语句,我们发现确实是在 ` join PostToCategories ` 后面增加了where条件。
    但是这种方法在面对多对多关系时,就**不够优雅**了。
    
     var post = await _repository.GetMyPostById(blogId, postId).AsNoTracking()
                            .Include(p => p.TagMap)
                            .Include(p => p.TagMap.Select(t => t.Tag))
                            .SingleOrDefaultAsync();
    

    改写成如下:

    var post = await _repository.GetMyPostById(blogId, postId).AsNoTracking()
                            .Select( p => new {
                                                    BlogPost = p,
                                                    TagMap = p.TagMap,
                                                    Tag = p.TagMap.Select(t => t.Tag),
                                                    }).SingleOrDefaultAsync();
    var blogPost = post.BlogPost;
    blogPost.TagMap = post.TagMap;
    blogPost.TagMap.ForEach(m => m.Tag = post.Tag.FirstOrDefault(g => g.Id == m.TagId));
    

    是不是感觉不美好了?

    那有没有更好的办法呢?stackoverflow上有人推荐了一个针对EF的扩展包 EntityFramework.DynamicFilters ,它的实现方式是在OnModelCreating的时候给Entity设置好过滤条件,当前DbContext对象涉及到该Entity类型的查询时,都会自动加上过滤条件。

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Filter("PostToCategory_IsActive", (PostToCategory ptc) => ptc.IsActive, true);//为PostToCategory类型设置过滤条件:IsActive==true
    }
    

    直接使用Include()就可以得到我们想要的结果,如下所示:

    var post = await _repository.GetMyPostById(blogId, postId).AsNoTracking()
                            .Include(p => p.PostToCategories)
                            .SingleOrDefaultAsync();
    

    可是这样以来所有的涉及到PostToCategories的查询都会被过滤,怎么办呢?EntityFramework.DynamicFilters提供 禁用/启动 过滤条件的API

    context.DisableFilter/EnableFilter("PostToCategory_IsActive");// 在当前DbContext实例对象中禁用/启用名为PostToCategory_IsActive的Filter
    modelBuilder.DisableFilterGlobally("PostToCategory_IsActive");// 全局禁用名为PostToCategory_IsActive的Filter
    context.DisableAllFilters();//禁用所有的Filters
    context.EnableAllFilters(); //启用所有的Filters

    于是代码就变成了这样:

    一、设置Filter

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Filter("PostToCategory_IsActive", (PostToCategory ptc) => ptc.IsActive, true);//为PostToCategory类型设置过滤条件:IsActive==true
    }
    

    二、全局禁用Filters

    context.DisableAllFilters();
    

    三、在需要过滤的地方启用Filter

    context.EnableFilter("PostToCategory_IsActive");
    var post = await _repository.GetMyPostById(blogId, postId).AsNoTracking()
                            .Include(p => p.PostToCategories)
                            .SingleOrDefaultAsync();
    
    

    而且还没有考虑DbContext缓存查询结果带来的问题。
    如果说第一种解决方案不优雅,那么这种方案就是恶心

    --- 2016.10.10 ---

    昨天借鉴了ABP的封装思想,把EntityFramework.DynamicFilters的API封装了下,确实美观多了。
    参见:

    本来顺风顺水,如tkb至简的博文中描述的那样,优雅的过滤数据,可惜最后在一次保存的时候出现了意外,最后发现原因出在和EntityFramework.Extended有冲突,因为后者是重新生成了SqlExpression,没有对EntityFramework.DynamicFilters在设置where条件中的@DynamicFilterParam_000001赋值。
    暂时没有想到更好的办法,遂先滚回去。:(

  • 相关阅读:
    golang 的几个入门资料
    docker 容器网络基础
    nginx 容器反向代理网址的设置
    【知乎Live】狼叔:如何正确的学习Node.js
    nginx资料汇总
    web 框架
    work behind corp proxy
    [转载] 历史上前端领域的重要技术革命
    前后端要不要分离以及如何做
    微电子工艺基础知识讲解(集成电路历史/厂商/产业链)
  • 原文地址:https://www.cnblogs.com/kexxxfeng/p/5925667.html
Copyright © 2020-2023  润新知