2017-03-20 74 views
12

我有一個擴展方法,可以讓你一般包括EF數據:多個包括()在EF核心

public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] 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<Patient, object>>[] includes) 
{ 
    return context.Patients 
     .IncludeMultiple(includes) 
     .FirstOrDefault(x => x.PatientId == id); 
} 

我相信擴展方法EF EF之前工作,但現在包括「兒童」是這樣做的:

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

有沒有辦法改變我的通用擴展方法來支持EF Core的新練習ThenInclude()

+0

只是做' .include(include)'爲你的數組中的第一個表達式,'current.ThenInclude(include)'爲剩餘的? – Evk

+0

@Evk你正在傳遞一個Linq表達式到這個擴展方法中,所以輸入是這樣的:'x => x.Posts.Select(p => p.Author)''。 – im1dermike

+0

我明白你的意思了。您是否測試過舊的語法在EF Core中無效?我聽說過它,但沒有測試自己。 – Evk

回答

4

正如comments所說的那樣,您可以帶上EF6 code來解析您的表達式並應用相關的Include/ThenInclude調用。它畢竟看起來不那麼辛苦,但由於這不是我的想法,所以我寧願不用代碼來回答它。

你可以改變你的模式來公開一些接口,允許你從調用者指定你的包含而不讓它訪問基礎查詢。

這將導致類似的信息(在using名稱空間必須與命名空間名稱包含在最後的代碼塊下面定義的擴展方法一致):

using YourProject.ExtensionNamespace; 

// ... 

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

GetById將是現在:

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

擴展方法IncludeMultiple

public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, 
    Func<IIncludable<T>, IIncludable> includes) 
    where T : class 
{ 
    if (includes == null) 
     return query; 

    var includable = (Includable<T>)includes(new Includable<T>(query)); 
    return includable.Input; 
} 

現在IncludeProvider類&接口,這是簡單的 「佔位符」 上附加擴展方法是不行的模仿EF IncludeThenInclude方法的工作:

public interface IIncludable { } 

public interface IIncludable<out TEntity> : IIncludable { } 

public interface IIncludable<out TEntity, out TProperty> : IIncludable<TEntity> { } 

internal class Includable<TEntity> : IIncludable<TEntity> where TEntity : class 
{ 
    internal IQueryable<TEntity> Input { get; } 

    internal Includable(IQueryable<TEntity> 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<TEntity, TProperty> : 
    Includable<TEntity>, IIncludable<TEntity, TProperty> 
    where TEntity : class 
{ 
    internal IIncludableQueryable<TEntity, TProperty> IncludableInput { get; } 

    internal Includable(IIncludableQueryable<TEntity, TProperty> queryable) : 
     base(queryable) 
    { 
     IncludableInput = queryable; 
    } 
} 

IIncludable擴展方法:

using Microsoft.EntityFrameworkCore; 

// others using ommitted 

namespace YourProject.ExtensionNamespace 
{ 
    public static class IncludableExtensions 
    { 
     public static IIncludable<TEntity, TProperty> Include<TEntity, TProperty>(
      this IIncludable<TEntity> includes, 
      Expression<Func<TEntity, TProperty>> propertySelector) 
      where TEntity : class 
     { 
      var result = ((Includable<TEntity>)includes).Input 
       .Include(propertySelector); 
      return new Includable<TEntity, TProperty>(result); 
     } 

     public static IIncludable<TEntity, TOtherProperty> 
      ThenInclude<TEntity, TOtherProperty, TProperty>(
       this IIncludable<TEntity, TProperty> includes, 
       Expression<Func<TProperty, TOtherProperty>> propertySelector) 
      where TEntity : class 
     { 
      var result = ((Includable<TEntity, TProperty>)includes) 
       .IncludableInput.ThenInclude(propertySelector); 
      return new Includable<TEntity, TOtherProperty>(result); 
     } 

     public static IIncludable<TEntity, TOtherProperty> 
      ThenInclude<TEntity, TOtherProperty, TProperty>(
       this IIncludable<TEntity, IEnumerable<TProperty>> includes, 
       Expression<Func<TProperty, TOtherProperty>> propertySelector) 
      where TEntity : class 
     { 
      var result = ((Includable<TEntity, IEnumerable<TProperty>>)includes) 
       .IncludableInput.ThenInclude(propertySelector); 
      return new Includable<TEntity, TOtherProperty>(result); 
     } 
    } 
} 

IIncludable<TEntity, TProperty>與EF幾乎相同,但不會延長IQueryable,並且不允許重新塑造查詢。

當然,如果調用者在同一個程序集中,它仍然可以將IIncludable投射到Includable並開始擺弄查詢。但是,如果有人想弄錯它,我們不可能阻止他這樣做(反射允許任何事情)。重要的是合同。

現在,如果你不在乎暴露IQueryable給調用者(我懷疑),顯然只是改變你的params論點的論據Func<Queryable<T>, Queryable<T>> addIncludes,避免編碼上述所有這些事情。

而最好的結局:我沒有測試過這個,我目前沒有使用實體框架!

+0

你完全測試了嗎?我無法讓它工作。我認爲有幾個錯別字... – im1dermike

+0

它編譯(從EF.Core IIncludableQueryable的本地副本),但它沒有測試。如果你在IIncludable擴展方法中進行了編譯,那麼你是否需要從EF使用'using'來使用EF include擴展方法? –

+0

Gotcha。我已經實現了所有的代碼,但是我無法使用Include lambda來調用存儲庫。智能感知不提供包括...... – im1dermike

2

Ofcourse有,

,你可以穿越原則params的表達式樹,以及任何嵌套包含,將其添加爲

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

但它不是小事,但絕對非常可行的,請分享,如果你這樣做,因爲它可以defintiely重用!

4

對於後人,另一個少口才,但更簡單的解決方案,利用使用navigationPropertyPathInclude()超載:

public static class BlogIncludes 
{ 
    public const string Posts = "Posts"; 
    public const string Author = "Posts.Author"; 
} 

internal static class DataAccessExtensions 
{ 
    internal static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> 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); 
-1
public Task<List<TEntity>> GetAll() 
    { 
     var query = _Db.Set<TEntity>().AsQueryable(); 
     foreach (var property in _Db.Model.FindEntityType(typeof(TEntity)).GetNavigations()) 
      query = query.Include(property.Name); 
     return query.ToListAsync(); 

    }