В API мне нужен динамический include, но EF Core не поддерживает String.
Из-за этого я создал mapper, который отображает строки в лямбда-выражения, добавленные в список, как:
List<List<Expression>> expressions = new List<List<Expression>>();
Рассмотрим следующие конкретные типы:
public class EFContext {
public DbSet<P1> P1s { get; set; }
public DbSet<P1> P2s { get; set; }
public DbSet<P1> P3s { get; set; }
}
public class P1 {
public P2 P2 { get; set; }
public P3 P3 { get; set; }
}
public class P2 {
public P3 P3 { get; set; }
}
public class P3 { }
Include и ThenInclude обычно используются следующим образом:
EFContext efcontext = new EFContext();
IQueryable<P1> result = efcontext.P1s.Include(p1 => p1.P2).ThenInclude(p2 => p2.P3).Include(p1 => p1.P3);
Их также можно использовать следующим образом:
Expression<Func<P1, P2>> p1p2 = p1 => p1.P2;
Expression<Func<P1, P3>> p1p3 = p1 => p1.P3;
Expression<Func<P2, P3>> p2p3 = p2 => p2.P3;
List<List<Expression>> expressions = new List<List<Expression>> {
new List<Expression> { p1p2, p1p3 },
new List<Expression> { p2p3 }
};
EFContext efcontext = new EFContext();
IIncludableQueryable<P1, P2> q1 = EntityFrameworkQueryableExtensions.Include(efcontext.P1s, p1p2);
IIncludableQueryable<P1, P3> q2 = EntityFrameworkQueryableExtensions.ThenInclude(q1, p2p3);
IIncludableQueryable<P1, P3> q3 = EntityFrameworkQueryableExtensions.Include(q2, p1p3);
result = q3.AsQueryable();
Проблема заключается в том, что мой метод получает список из списка выражений, и у меня есть только базовый тип в T:
public static class IncludeExtensions<T> {
public static IQueryable<T> IncludeAll(this IQueryable<T> collection, List<List<Expression>> expressions) {
MethodInfo include = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethods(nameof(EntityFrameworkQueryableExtensions.Include)).Single(mi => mi.GetParameters().Any(pi => pi.Name == "navigationPropertyPath"));
MethodInfo includeAfterCollection = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethods(nameof(EntityFrameworkQueryableExtensions.ThenInclude)).Single(mi => !mi.GetParameters()[0].ParameterType.GenericTypeArguments[1].IsGenericParameter);
MethodInfo includeAfterReference = typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethods(nameof(EntityFrameworkQueryableExtensions.ThenInclude)).Single(mi => mi.GetParameters()[0].ParameterType.GenericTypeArguments[1].IsGenericParameter);
foreach (List<Expression> path in expressions) {
Boolean start = true;
foreach (Expression expression in path) {
if (start) {
MethodInfo method = include.MakeGenericMethod(typeof(T), ((LambdaExpression)expression).ReturnType);
IIncludableQueryable<T,?> result = method.Invoke(null, new Object[] { collection, expression });
start = false;
} else {
MethodInfo method = includeAfterReference.MakeGenericMethod(typeof(T), typeof(?), ((LambdaExpression)expression).ReturnType);
IIncludableQueryable <T,?> result = method.Invoke(null, new Object[] { collection, expression });
}
}
}
return collection; // (to be replaced by final as Queryable)
}
}
Основная проблема заключается в разрешении правильных типов для каждого шага Include и ThenInclude, а также который ThenInclude для использования...
Возможно ли это с нынешним ядром EF7? Кто-нибудь нашел решение для динамического включения?
Включить и ThenIncludeAfterReference и ThenIncludeAfterCollection являются частью EntityFrameworkQueryableExtensions класса в репозитории EntityFramework Github.