entity framework中的条件包含()

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

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

我在这里读过Filtered Projection

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

但我无法理解它。

我不希望在最终的where子句中包含filter,因为我想要所有post,但我只想检索属于作者的那些post的附件。

编辑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 Attachments { get; set; } public List Comments { get; set; } } 

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

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

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

上面的代码仅包含Attachment.OwnerId == 1

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

public ICollection Attachments { get; set; }

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

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

关系修正意味着您可以通过Post的导航属性访问这些已过滤的附件

第二种方法:对数据库进行一次查询,然后进行内存中查询:

 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 postsWithAuthoredAttachments = //query as above but use new PostWithAuthoredAttachments in the Select 

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

 List 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() 

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

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

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

…然后就像使用它一样使用它。包括,例如

 bool yourCondition; .ConditionalInclude(yourCondition)