How to write Repository method for .ThenInclude in EF Core 2

前端 未结 3 1824
攒了一身酷
攒了一身酷 2020-12-08 03:22

I\'m trying to write a repository method for Entity Framework Core 2.0 that can handle returning child collections of properties using .ThenInclude, but I\'m having trouble

3条回答
  •  醉梦人生
    2020-12-08 04:03

    I had the same issue since EF Core doesn't support lazy loading but i tried to get workaround in the following way:

    First create an attribute class to mark our desired navigation properties from other properties of a given class.

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

    Extension methods to filter out navigation properties and apply Include/ThenInclude using string based Eager loading.

    public static class DbContextHelper
    {
    
        public static Func, IQueryable> GetNavigations() where T : BaseEntity
        {
            var type = typeof(T);
            var navigationProperties = new List();
    
            //get navigation properties
            GetNavigationProperties(type, type, string.Empty, navigationProperties);
    
            Func, IQueryable> includes = ( query => {
                        return  navigationProperties.Aggregate(query, (current, inc) => current.Include(inc));   
                });
    
            return includes;
        }
    
        private static void GetNavigationProperties(Type baseType, Type type, string parentPropertyName, IList 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);
                }
            }
        }
    }
    

    Sample POCO classes implementing 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; }
    }
    

    Usage in Repository

    public async Task GetAsync(Expression> predicate)
    {
        Func, IQueryable> includes = DbContextHelper.GetNavigations();
        IQueryable query = _context.Set();
        if (includes != null)
        {
            query = includes(query);
        }
    
        var entity = await query.FirstOrDefaultAsync(predicate);
        return entity;
    }
    

    Json result for sample class C would be:

    {
      "B" : {
            "A" : {
                  "Prop" : "SOME_VALUE"
                }
          }
    } 
    

提交回复
热议问题