5

.ThenIncludeを使用して子コレクションの子コレクションを処理できるEntity Framework Core 2.0用のリポジトリメソッドを作成しようとしていますが、表現。ここでは、.Includeのための作業メソッドがあります。これは、エンティティの子プロパティ(lambdaのリストを提供します)を返します。今ここにEFコア2の.ThenIncludeのリポジトリメソッドの記述方法

public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties) 
{ 
    IQueryable<T> query = _context.Set<T>(); 
    foreach (var includeProperty in includeProperties) 
    { 
     query = query.Include(includeProperty); 
    } 

    return query.Where(predicate).FirstOrDefault(); 
} 

は、2つの式のタプルを取り、.INCLUDE(A => a.someChild).ThenInclude(B => b.aChildOfSomeChild)チェーンにそれらを供給するメソッドを書く時、私の試みです。これは完璧な解決策ではありません。なぜなら、子どもの1人の子供しか処理しないからです。

public T GetSingle(Expression<Func<T, bool>> predicate, params Tuple<Expression<Func<T, object>>, Expression<Func<T, object>>>[] includeProperties) 
{ 
    IQueryable<T> query = _context.Set<T>(); 
    foreach (var includeProperty in includeProperties) 
    { 
     query = query.Include(includeProperty.Item1).ThenInclude(includeProperty.Item2);    
    } 

    return query.Where(predicate).FirstOrDefault(); 
} 

Intellisenseは、「型を推論することはできません。型を明示的に指定してください」というエラーを返します。 Item2の表現は、Item1に何らかの形で関連する必要があるため、それが持っている子関係について知る必要があるため、それが気になります。

このような方法を書くためのアイデアやより良い技術はありますか?

+1

それは事実上、所望のEF6を使用してリポジトリ方法に含ま特定の標準であったので、これを複数回求められています。 EFCチームのメンバーの話を聞いて興味深いのは、パターンを「インクルード」/「ThenInclude」に変更する決定の背後にある理由で、明らかにこのように表現できないことです。さらに重要なのは、EFCの置き換えです。 –

答えて

3

私は、この再発見しました私は欲しいものを正確に実行します。ヤレの答えは良かったけど、そこにはない。

/// <summary> 
    /// Gets the first or default entity based on a predicate, orderby delegate and include delegate. This method default no-tracking query. 
    /// </summary> 
    /// <param name="selector">The selector for projection.</param> 
    /// <param name="predicate">A function to test each element for a condition.</param> 
    /// <param name="orderBy">A function to order elements.</param> 
    /// <param name="include">A function to include navigation properties</param> 
    /// <param name="disableTracking"><c>True</c> to disable changing tracking; otherwise, <c>false</c>. Default to <c>true</c>.</param> 
    /// <returns>An <see cref="IPagedList{TEntity}"/> that contains elements that satisfy the condition specified by <paramref name="predicate"/>.</returns> 
    /// <remarks>This method default no-tracking query.</remarks> 
    public TResult GetFirstOrDefault<TResult>(Expression<Func<TEntity, TResult>> selector, 
               Expression<Func<TEntity, bool>> predicate = null, 
               Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, 
               Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null, 
               bool disableTracking = true) 
    { 
     IQueryable<TEntity> query = _dbSet; 
     if (disableTracking) 
     { 
      query = query.AsNoTracking(); 
     } 

     if (include != null) 
     { 
      query = include(query); 
     } 

     if (predicate != null) 
     { 
      query = query.Where(predicate); 
     } 

     if (orderBy != null) 
     { 
      return orderBy(query).Select(selector).FirstOrDefault(); 
     } 
     else 
     { 
      return query.Select(selector).FirstOrDefault(); 
     } 
    } 

使用:

 var affiliate = await affiliateRepository.GetFirstOrDefaultAsync(
      predicate: b => b.Id == id, 
      include: source => source 
       .Include(a => a.Branches) 
       .ThenInclude(a => a.Emails) 
       .Include(a => a.Branches) 
       .ThenInclude(a => a.Phones)); 
+1

これは機能しますが、望ましくない可能性があるEntity Frameworkクラスを公開します。 – SebastianR

1

EFコアは遅延ロードをサポートしていないので、私は同じ問題を持っていたが、私は次のように回避策を取得しようとしました:

まず与えられたの他のプロパティから、当社の希望ナビゲーションプロパティをマークする属性クラスを作成しますクラス。

[AttributeUsage(AttributeTargets.Property, Inherited = false)] 
public class NavigationPropertyAttribute : Attribute 
{ 
    public NavigationPropertyAttribute() 
    { 
    } 
} 

ナビゲーションプロパティをフィルタリングし、文字列ベースのEager読み込みを使用してInclude/ThenIncludeを適用する拡張メソッド。リポジトリ内NavigationPropertyAttribute

public class A : BaseEntity{ 
    public string Prop{ get; set; } 
} 

public class B : BaseEntity{ 
    [NavigationProperty] 
    public virtual A A{ get; set; } 
} 

public class C : BaseEntity{ 
    [NavigationProperty] 
    public virtual B B{ get; set; } 
} 

使用法を実装

public static class DbContextHelper 
    { 

     public static Func<IQueryable<T>, IQueryable<T>> GetNavigations<T>() where T : BaseEntity 
     { 
     var type = typeof(T); 
     var navigationProperties = new List<string>(); 

     //get navigation properties 
     GetNavigationProperties(type, type, string.Empty, navigationProperties); 

     Func<IQueryable<T>, IQueryable<T>> includes = (query => { 
        return navigationProperties.Aggregate(query, (current, inc) => current.Include(inc)); 
      }); 

     return includes; 
    } 

    private static void GetNavigationProperties(Type baseType, Type type, string parentPropertyName, IList<string> accumulator) 
    { 
     //get navigation properties 
     var properties = type.GetProperties(); 
     var navigationPropertyInfoList = properties.Where(prop => prop.IsDefined(typeof(NavigationPropertyAttribute))); 

     foreach (PropertyInfo prop in navigationPropertyInfoList) 
     { 
      var propertyType = prop.PropertyType; 
      var elementType = propertyType.GetTypeInfo().IsGenericType ? propertyType.GetGenericArguments()[0] : propertyType; 

      //Prepare navigation property in {parentPropertyName}.{propertyName} format and push into accumulator 
      var properyName = string.Format("{0}{1}{2}", parentPropertyName, string.IsNullOrEmpty(parentPropertyName) ? string.Empty : ".", prop.Name); 
      accumulator.Add(properyName); 

      //Skip recursion of propert has JsonIgnore attribute or current property type is the same as baseType 
      var isJsonIgnored = prop.IsDefined(typeof(JsonIgnoreAttribute)); 
      if(!isJsonIgnored && elementType != baseType){ 
       GetNavigationProperties(baseType, elementType, properyName, accumulator); 
      } 
     } 

    } 

サンプルPOCOクラスが

public async Task<T> GetAsync(Expression<Func<T, bool>> predicate) 
{  
    Func<IQueryable<T>, IQueryable<T>> includes = DbContextHelper.GetNavigations<T>(); 
    IQueryable<T> query = _context.Set<T>(); 
    if (includes != null) 
    { 
     query = includes(query); 
    } 

    var entity = await query.FirstOrDefaultAsync(predicate); 
    return entity; 
} 
サンプルクラスCのための

JSONの結果は次のようになります。

{ 
    "B" : { 
     "A" : { 
       "Prop" : "SOME_VALUE" 
      } 
     } 
} 
関連する問題