【问题标题】:Conditional Include() in Entity Framework [duplicate]实体框架中的条件包含()
【发布时间】:2015-12-21 11:18:06
【问题描述】:

我已经看到了一些类似问题的答案,但是我似乎无法弄清楚如何将答案应用于我的问题。

var allposts = _context.Posts
            .Include(p => p.Comments)
            .Include(aa => aa.Attachments)
            .Include(a => a.PostAuthor)
            .Where(t => t.PostAuthor.Id == postAuthorId).ToList();

附件可以由作者(作者类型)或贡献者(贡献者类型)上传。我想要做的只是获取附件所有者为作者类型的附件。

我知道这不起作用并给出错误:

.Include(s=>aa.Attachments.Where(o=>o.Owner is Author))

我在这里阅读了过滤投影

编辑 - 文章链接: :http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx

但我就是想不通。

我不想在最后的 where 子句中包含过滤器,因为我想要所有帖子,但我只想检索属于作者的帖子的附件。

编辑 2:- 请求发布架构

public abstract class Post : IPostable
{

    [Key]
    public int Id { get; set; }

    [Required]
    public DateTime PublishDate { get; set; }

    [Required]
    public String Title { get; set; }

    [Required]
    public String Description { get; set; }

    public Person PostAuthor { get; set; }
    public virtual ICollection<Attachment> Attachments { get; set; }
    public List<Comment> Comments { get; set; }
}

【问题讨论】:

  • 你能告诉我们Posts架构吗?
  • @DarkKnight - 见编辑
  • @grayson 你要求做的事情是不可能的。 Linq2Sql 会将您的代码转换为原始的SQL,并通过连接返回子行。你不能在SQL 中进行这种有条件的加入。您唯一的选择是删除.Include(aa =&gt; aa.Attachments),并进行第二个查询,根据所有者是否是作者/贡献者来返回附件。

标签: c# entity-framework linq linq-to-entities


【解决方案1】:

对于网络核心

https://docs.microsoft.com/ru-ru/ef/core/querying/related-data/explicit

var allposts = _context.Posts
        .Include(p => p.Comments)
        .Include(a => a.PostAuthor)
        .Where(t => t.PostAuthor.Id == postAuthorId).ToList();

_context.Entry(allposts)
        .Collection(e => e.Attachments)
        .Query()
        .Where(e=> e.Owner is Author)
        .Load();

它对 sql 进行 2 次查询。

【讨论】:

  • 这我不起作用,因为您尝试在帖子列表上使用显式加载。但是 .Entry() 只能用于单个实体。
【解决方案2】:

试试这个

var allposts = _context.Posts
        .Include(p => p.Comments)
        .Include(a => a.PostAuthor)
        .Where(t => t.PostAuthor.Id == postAuthorId).ToList();

_context.Attachments.Where(o=>o.Owner is Author).ToList();

【讨论】:

    【解决方案3】:

    EF Core 5.0 即将引入过滤包含。

    var blogs = context.Blogs
        .Include(e => e.Posts.Where(p => p.Title.Contains("Cheese")))
        .ToList();
    

    参考:https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew#filtered-include

    【讨论】:

      【解决方案4】:

      假设“a”属于“YourType”类型,则可以通过使用方法扩展来解决条件包含,例如

      public static class QueryableExtensions
      {
          public static IQueryable<T> ConditionalInclude<T>(this IQueryable<T> source, bool include) where T : YourType
          {
              if (include)
              {
                  return source
                      .Include(a => a.Attachments)
                      .Include(a => a.Attachments.Owner));
              }
      
              return source;
          }
      }
      

      ...然后就像使用.Include一样使用它,例如

      bool yourCondition;
      
      .ConditionalInclude(yourCondition)
      

      【讨论】:

      • 这是玩笑还是什么?
      • 那是天才..!
      • 这对我有帮助!
      【解决方案5】:

      Attachments 导航属性中删除virtual 关键字以防止延迟加载:

      public ICollection&lt;Attachment&gt; Attachments { get; set; }

      第一种方法:发出两个单独的查询:一个用于帖子,一个用于附件,然后让关系修复完成其余的工作:

      List<Post> postsWithAuthoredAttachments = _context.Posts
          .Include(p => p.Comments) 
          .Include(p => p.PostAuthor)
          .Where(p => p.PostAuthor.Id == postAuthorId)
          .ToList();
      
      List<Attachment> filteredAttachments = _context.Attachments
          .Where(a => a.Post.PostAuthor.Id == postAuthorId)
          .Where(a => a.Owner is Author)
          .ToList()
      

      关系修复意味着您可以通过帖子的导航属性访问这些过滤后的附件

      第二种方法:先查询数据库,然后再进行内存查询:

      var query = _context.Posts
          .Include(p => p.Comments) 
          .Include(p => p.PostAuthor)
          .Where(p => p.PostAuthor.Id == postAuthorId)
          .Select(p => new 
              {
                  Post = p,
                  AuthoredAttachments = p.Attachments
                      Where(a => a.Owner is Author)
              }
          );
      

      我这里只使用匿名类型

      var postsWithAuthoredAttachments = query.ToList()
      

      或者我会创建一个 ViewModel 类来避免匿名类型:

      List<MyDisplayTemplate> postsWithAuthoredAttachments = 
           //query as above but use new PostWithAuthoredAttachments in the Select
      

      或者,如果你真的想打开帖子:

      List<Post> postsWithAuthoredAttachments = query.//you could "inline" this variable
          .AsEnumerable() //force the database query to run as is - pulling data into memory
          .Select(p => p) //unwrap the Posts from the in-memory results
          .ToList()
      

      【讨论】:

        【解决方案6】:

        您可以使用this implementation 的扩展方法(例如)Include2()。之后,您可以调用:

        _context.Posts.Include2(post => post.Attachments.Where(a => a.OwnerId == 1))
        

        上面的代码只包含Attachment.OwnerId == 1的附件。

        【讨论】:

        • 嗨,当帖子没有附件时,这似乎不起作用。 A,我错过了一些东西
        【解决方案7】:

        从您发布的链接中,我可以确认该技巧有效,但仅适用于一对多(或多对一)关系。在这种情况下,您的 Post-Attachment 应该是一对多的关系,所以它完全适用。这是您应该有的查询:

        //this should be disabled temporarily
        _context.Configuration.LazyLoadingEnabled = false;
        var allposts = _context.Posts.Where(t => t.PostAuthor.Id == postAuthorId)
                               .Select(e => new {
                                   e,//for later projection
                                   e.Comments,//cache Comments
                                   //cache filtered Attachments
                                   Attachments = e.Attachments.Where(a => a.Owner is Author),
                                   e.PostAuthor//cache PostAuthor
                                })
                               .AsEnumerable()
                               .Select(e => e.e).ToList();
        

        【讨论】:

        • 是否需要添加“_context.Configuration.LazyLoadingEnabled = true;”在此调用后重新启用延迟加载?
        • @grayson 是的,如果你想重新启用它。
        • @Hopeless 知道这个问题很老,但我最近的问题被标记为与这个问题重复,我的要求是当Attachments 有其他子属性时,它们在结果中不可用。链接到我的问题stackoverflow.com/questions/58347487/…
        • @Krtti 我不太确定您的问题是否可以通过此处回答的内容来解决,实际上我不太了解您的问题(根据您在链接问题中的描述,至少有2 种理解方式)。也有人在那里添加了一个答案,看起来它对于理解问题的一种方式是正确的。我的回答中的想法很简单,可以应用,当它不起作用时,它可能不适用。最后,我很久没有使用 EF 和 Linq-to-entity 了,所以我可能帮不了你太多。
        • @Hopeless 别担心,我的问题已得到解答,感谢您抽出宝贵时间告知
        【解决方案8】:

        Include() 中的 Lambda 只能指向一个属性:

        .Include(a => a.Attachments)
        .Include(a => a.Attachments.Owner);
        

        你的情况对我来说没有意义,因为Include() 表示join,你要么做,要么不做。而且不是有条件的。

        你会如何用原始 SQL 编写这个?


        为什么不只是这样:

        context.Attachments
               .Where(a => a.Owner.Id == postAuthorId &&
                           a.Owner.Type == authorType);
        

        ?

        【讨论】:

        • 谢谢。我对这一切都很陌生。我可以这样做来获取所需类型的附件,但我不知道如何将其合并回 Post。我的 DisplayTemplate 派生自 Post 并显示 Post.Attachments
        • 在原始 SQL 中如何编写:select * from table1 x join table2 y on x.fkey=y.fkey and (condition with y)
        • @Austin_Anderson:这不是条件连接,是吗?
        • 连接总是有条件的。它通常以 pkeys 为条件。但是,您可以完全加入 pkey 和其他条件。事实上,因为在大型数据集中通常首选 JOINS。
        • @DiscipleMichael:加入永远不是有条件的,它要么存在,要么不存在。并且执行一个可能是昂贵的。良好的性能调整是消除您知道不会产生任何结果的连接。在 U-SQL 中,它通常使用动态 SQL 来完成。但这是关于 EF 的问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-02-15
        • 2018-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-22
        相关资源
        最近更新 更多