5

Tôi đang cố gắng viết một phương thức kho lưu trữ cho Entity Framework Core 2.0 có thể xử lý các bộ sưu tập các thuộc tính con trả về bằng cách sử dụng .ThenInclude, nhưng tôi đang gặp rắc rối với thứ hai biểu hiện. Đây là một phương thức làm việc cho .Include, nó sẽ trả về các thuộc tính con (bạn cung cấp một danh sách các lambdas) của thực thể của bạn.Cách viết phương thức Repository cho .ThenInclude trong EF Core 2

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

Bây giờ đây là nỗ lực của tôi lúc viết một phương pháp mà sẽ mất một tuple của hai biểu thức và nuôi những thành .Include (a => a.someChild) chuỗi .ThenInclude (b => b.aChildOfSomeChild) . Đây không phải là một giải pháp hoàn hảo bởi vì nó chỉ xử lý một đứa trẻ của một đứa trẻ, nhưng đó là một sự khởi đầu.

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 trả về lỗi "Loại không thể suy ra từ mức sử dụng, hãy thử chỉ rõ loại". Tôi có cảm giác đó là vì biểu thức trong Item2 cần phải được phân loại như bằng cách nào đó liên quan đến Item1, bởi vì nó cần phải biết về mối quan hệ con của nó.

Bất kỳ ý tưởng hay kỹ thuật nào tốt hơn để viết một phương pháp như thế này?

+1

này đã được hỏi nhiều lần kể từ khi nó là de facto một tiêu chuẩn của quy định cụ thể mong muốn bao gồm các phương pháp sử dụng kho EF6. Thật thú vị khi nghe một số thành viên nhóm EFC lý do đằng sau quyết định thay đổi mẫu thành 'Include' /' ThenInclude' mà dường như không thể đại diện theo cách này, và quan trọng hơn, thay thế EFC là gì. –

Trả lời

3

tôi thấy lại này phương pháp pository trực tuyến và nó thực hiện chính xác những gì tôi muốn. Câu trả lời của Yared là tốt, nhưng không phải tất cả các con đường ở đó.

/// <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(); 
     } 
    } 

Cách sử dụng:

 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

Trong khi điều này làm việc, nó cho thấy một lớp khung Entity, mà có thể không được mong muốn. – SebastianR

1

tôi đã cùng một vấn đề kể từ EF Core không hỗ trợ tải lười biếng nhưng tôi đã cố gắng để có được cách giải quyết theo cách sau:

Đầu tiên tạo ra một lớp thuộc tính để đánh dấu tài sản chuyển hướng mong muốn của chúng tôi từ các tài sản khác của một định lớp học.

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

Phương pháp mở rộng để lọc ra các thuộc tính điều hướng và áp dụng Bao gồm/Sau đóBao gồm việc sử dụng chuỗi Tải dựa trên tải.

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

    } 

lớp mẫu POCO thực hiện 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; } 
} 

Cách sử dụng trong Repository

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

Json kết quả cho lớp mẫu C sẽ là:

{ 
    "B" : { 
     "A" : { 
       "Prop" : "SOME_VALUE" 
      } 
     } 
} 
Các vấn đề liên quan