实体框架中的条件Include()

问题描述 投票:21回答:6

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

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; }
}
c# entity-framework linq linq-to-entities
6个回答
14
投票

从您发布的链接中,我可以确认该技巧有效,但仅适用于一对多(或多对一)关系。在这种情况下,您的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();

5
投票

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

public ICollection<Attachment> 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()

3
投票

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

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

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


0
投票

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


-3
投票

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);


-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)
    
© www.soinside.com 2019 - 2024. All rights reserved.