EF Core中的多个Includes()

我有一个扩展方法,允许您在EF中一般包含数据:

public static IQueryable IncludeMultiple(this IQueryable query, params Expression<Func>[] includes) where T : class { if (includes != null) { query = includes.Aggregate(query, (current, include) => current.Include(include)); } return query; } 

这允许我在我的存储库中有这样的方法:

 public Patient GetById(int id, params Expression<Func>[] includes) { return context.Patients .IncludeMultiple(includes) .FirstOrDefault(x => x.PatientId == id); } 

我相信扩展方法在EF Core之前有效,但现在包括“children”就像这样:

 var blogs = context.Blogs .Include(blog => blog.Posts) .ThenInclude(post => post.Author); 

有没有办法改变我的通用扩展方法,以支持EF Core的新ThenInclude()实践?

正如其他人的评论所述,您可以使用EF6代码来解析表达式并应用相关的Include / ThenInclude调用。 毕竟它看起来并不那么难,但由于这不是我的想法,我宁愿不用它的代码回答。

您可以更改模式以显示某些界面,从而允许您从调用者指定包含而不让它访问底层可查询。

这将导致类似于:

 using YourProject.ExtensionNamespace; // ... patientRepository.GetById(0, ip => ip .Include(p => p.Addresses) .ThenInclude(a=> a.Country)); 

using命名空间必须与包含最后一个代码块中定义的扩展方法的命名空间名称匹配。

GetById现在是:

 public static Patient GetById(int id, Func, IIncludable> includes) { return context.Patients .IncludeMultiple(includes) .FirstOrDefault(x => x.EndDayID == id); } 

扩展方法IncludeMultiple

 public static IQueryable IncludeMultiple(this IQueryable query, Func, IIncludable> includes) where T : class { if (includes == null) return query; var includable = (Includable)includes(new Includable(query)); return includable.Input; } 

Includable类和接口,它们是简单的“占位符”,其他附加扩展方法将用于模拟EF IncludeThenInclude方法:

 public interface IIncludable { } public interface IIncludable : IIncludable { } public interface IIncludable : IIncludable { } internal class Includable : IIncludable where TEntity : class { internal IQueryable Input { get; } internal Includable(IQueryable queryable) { // C# 7 syntax, just rewrite it "old style" if you do not have Visual Studio 2017 Input = queryable ?? throw new ArgumentNullException(nameof(queryable)); } } internal class Includable : Includable, IIncludable where TEntity : class { internal IIncludableQueryable IncludableInput { get; } internal Includable(IIncludableQueryable queryable) : base(queryable) { IncludableInput = queryable; } } 

IIncludable扩展方法:

 using Microsoft.EntityFrameworkCore; // others using ommitted namespace YourProject.ExtensionNamespace { public static class IncludableExtensions { public static IIncludable Include( this IIncludable includes, Expression> propertySelector) where TEntity : class { var result = ((Includable)includes).Input .Include(propertySelector); return new Includable(result); } public static IIncludable ThenInclude( this IIncludable includes, Expression> propertySelector) where TEntity : class { var result = ((Includable)includes) .IncludableInput.ThenInclude(propertySelector); return new Includable(result); } public static IIncludable ThenInclude( this IIncludable> includes, Expression> propertySelector) where TEntity : class { var result = ((Includable>)includes) .IncludableInput.ThenInclude(propertySelector); return new Includable(result); } } } 

IIncludable几乎就像来自EF的IIncludableQueryable ,但它不扩展IQueryable并且不允许重新整形查询。

当然,如果调用者在同一个程序IIncludable ,它仍然可以将IIncludableIncludable并开始摆弄可查询对象。 但是,如果有人想弄错,我们就无法阻止他这样做(reflection允许任何事情)。 重要的是暴露的合同。

现在,如果您不关心将IQueryable暴露给调用者(我怀疑),显然只需更改您的params参数以获取Func, Queryable> addIncludes参数,并避免对上述所有内容进行编码。

最好的结果:我没有测试过这个,我目前不使用Entity Framework!

对于后代而言,另一个不太有说服力但更简单的解决方案是利用使用navigationPropertyPathInclude()重载:

 public static class BlogIncludes { public const string Posts = "Posts"; public const string Author = "Posts.Author"; } internal static class DataAccessExtensions { internal static IQueryable IncludeMultiple(this IQueryable query, params string[] includes) where T : class { if (includes != null) { query = includes.Aggregate(query, (current, include) => current.Include(include)); } return query; } } public Blog GetById(int ID, params string[] includes) { var blog = context.Blogs .Where(x => x.BlogId == id) .IncludeMultiple(includes) .FirstOrDefault(); return blog; } 

存储库调用是:

 var blog = blogRepository.GetById(id, BlogIncludes.Posts, BlogIncludes.Author); 

当然有,

你可以遍历原始参数的表达式树,以及任何嵌套的包含,将它们添加为

  .Include(entity => entity.NavigationProperty) .ThenInclude(navigationProperty.NestedNavigationProperty) 

但它不是微不足道的,但绝对是非常可行的,请分享,如果你这样做,因为它可以明确地重复使用!

你可以这样做:

 public Patient GetById(int id, Func, IIncludableQueryable> includes = null) { IQueryable queryable = context.Patients; if (includes != null) { queryable = includes(queryable); } return queryable.FirstOrDefault(x => x.PatientId == id); } var patient = GetById(1, includes: source => source.Include(x => x.Relationship1).ThenInclude(x => x.Relationship2)); 
 public Task> GetAll() { var query = _Db.Set().AsQueryable(); foreach (var property in _Db.Model.FindEntityType(typeof(TEntity)).GetNavigations()) query = query.Include(property.Name); return query.ToListAsync(); }