1

У меня есть следующий метод. Я использую анонимную проекцию для фильтрации включений EF. Я узнал этот метод из этого BlogPost: http://thedatafarm.com/data-access/use-projections-and-a-repository-to-fake-a-filtered-eager-load/Как я могу реорганизовать этот метод с использованием анонимной проекции, чтобы быть более общим?

public IEnumerable<Entities.Nutrient> FindAllForSpecificLanguage(bool overridePossibleLogicalDelete) 
{ 
    using (var context = CreateObjectContext()) 
    { 
     context.ContextOptions.LazyLoadingEnabled = false; 
     Entities.Nutrient[] result; 

     var list = context.Nutrients 
      .Select(nut => new 
      { 
       Entity = nut, 
       Descriptions = nut.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode)) 
      }).ToList(); //perform query 
     var resultList = list 
      .Select(entity => entity.Entity); 

     return resultList; 
    } 
} 

Этот метод должен быть встроен во все услуги (API-интерфейс поддерживает около 30 языков, на данный момент у нас есть много накладных расходов DB ...). Я пытаюсь построить его в общем виде, но я ужасно неопытен в деревьях выражений. Я думал, что полностью воссоздал эту функцию, но мне что-то не хватает, потому что она не работает. Это то, что я до сих пор:

public virtual IEnumerable<TEntity> FindAllForSpecificLanguage(bool overridePossibleLogicalDelete, Expression<Func<TEntity, IEnumerable<object>>> selectEntityDescriptions) 
{ 
    using (var context = CreateObjectContext()) 
    { 
     context.ContextOptions.LazyLoadingEnabled = false; 
     ObjectQuery<TEntity> queryObjectSet = GetObjectSet(context); 
     TEntity[] result; 

     Type anonType = new {Entity = default(TEntity), Descriptions = Enumerable.Empty<object>()}.GetType(); 

     // (entityManagerBaseEntity) => new { Entity = entityManagerBaseEntity, Descriptions = selectEntityDescriptions(entityManagerBaseEntity) } 
     // 1) "(entityManagerBaseEntity) =>" 
     var pe = Expression.Parameter(typeof(TEntity), "entityManagerBaseEntity"); 
     // 2) "selectEntityDescriptions(entityManagerBaseEntity)" 
     var exprFunc = Expression.Invoke(selectEntityDescriptions, pe); 
     // get constructor for anonymous type 
     var constructorInfo = anonType.GetConstructor(new[] { typeof(TEntity), typeof(IEnumerable<object>) }); 
     // 3) "new AnonType(entityManagerBaseEntity, exprFunc(entityManagerBaseEntity))" 
     var constructAnonType = Expression.New(constructorInfo, pe, exprFunc); 
     // 4) combine all to a lambda 
     // {entity => new <>f__AnonymousType0`2(entity, Invoke(entity => entity.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode)), entity))} 
     var cooleExpression = Expression.Lambda<Func<TEntity, dynamic>>(constructAnonType, pe); 
     //var bla = cooleExpression.Compile(); 
     //var list = queryObjectSet.AsQueryable().Provider.CreateQuery<dynamic>(cooleExpression).ToList(); 
     var list = queryObjectSet.Select(cooleExpression).ToList(); //perform query 
     var resultList = list 
      .Select(entity => entity.Entity as TEntity); 

     return resultList; 
    } 
} 

(примечание: CreateObjectContext и GetObjectSet прекрасно методы работы)

который следовало бы назвать так:

_nutrientManager.FindAllForSpecificLanguage(true, (entity) => entity.Descriptions.Where(desc => desc.LanguageCode.Equals(DataLanguageContext.Current.DataLanguageCode))) 

Выражение, которое будет построен набирается в комментариях. Я думаю, что это нормально, но соединение никогда не выполняется. Если я отладки я получаю следующую трассировку стека:

System.NotSupportedException: Только без параметров Конструкторы и Инициализаторы поддерживается в LINQ к Entities. в System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.NewTranslator.TypedTranslate (родитель ExpressionConverter, newExpression linq) в System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate (родитель ExpressionConverter , Expression LINQ)

ответ

1

Так .. несколько вещей:

  1. Вы не можете вызывать функции в выражении электронной L2E
  2. Вы можете использовать только конструкторы без параметров
  3. Вы можете построить целый раздел, где себя в выражении

весь раствор можно найти здесь: https://github.com/YoeriVD/entity-framework-filter-on-include/blob/master/ExpressionTrees/Program.cs

важный кусок:

private static void Main(string[] args) 
    { 
     FilterOnInclude<Car, Wheel>(car => car.Wheels, wheel => wheel.SizeInInches == 14) 
      .ForEach(car => Console.WriteLine($"car : {car.Name} wheels: {car.Wheels.Count}")); 
    } 

    private static IEnumerable<TEntity> FilterOnInclude<TEntity, TChildEntity>(
     Expression<Func<TEntity, IEnumerable<TChildEntity>>> propertyExpression, 
     Expression<Func<TChildEntity, bool>> predicateExpression) 
     where TEntity : class 
    { 
     using (var context = new CarContext()) 
     { 
      context.Configuration.LazyLoadingEnabled = false; 

      var selector = CreateSelector(propertyExpression, predicateExpression); 
      return 
       context.Set<TEntity>().Select(
        selector 
        ).ToList().Select(e => e.Entity).ToArray(); 
     } 
    } 

    private static Expression<Func<TEntity, EntityWithFilteredChildren<TEntity, TChildEntity>>> CreateSelector 
     <TEntity, TChildEntity>(
     Expression<Func<TEntity, IEnumerable<TChildEntity>>> propertyExpression, 
     Expression<Func<TChildEntity, bool>> predicateExpression) 
    { 
     var selectType = typeof (EntityWithFilteredChildren<TEntity, TChildEntity>); 

     //bind entity 
     var entityValueParam = Expression.Parameter(typeof (TEntity), "entityValue"); 
     var entityProp = selectType.GetProperty("Entity"); 
     var entityValueAssignment = Expression.Bind(
      entityProp, entityValueParam); 
     //bind collection 
     var childrenProp = selectType.GetProperty("Children"); 
     var descriptionsMemberExpression = (propertyExpression.Body as MemberExpression); 
     var descriptionsPropertyInfo = (PropertyInfo) descriptionsMemberExpression.Member; 
     var descriptionsProperty = Expression.Property(entityValueParam, descriptionsPropertyInfo); 
     //perform where call 
     var whereCall = Expression.Call(typeof (Enumerable), "Where", new[] {typeof (TChildEntity)}, descriptionsProperty, 
      predicateExpression); 

     var descriptionValueAssignment = Expression.Bind(
      childrenProp, whereCall); 

     var ctor = Expression.New(selectType); 
     var memberInit = Expression.MemberInit(ctor, entityValueAssignment, descriptionValueAssignment); 
     var selector = Expression.Lambda<Func<TEntity, EntityWithFilteredChildren<TEntity, TChildEntity>>>(memberInit, 
      entityValueParam); 

     return selector; 
    } 

    public class EntityWithFilteredChildren<T, TChild> 
    { 
     public T Entity { get; set; } 
     public IEnumerable<TChild> Children { get; set; } 
    } 
+0

Что такое $ в вашей консоли. WriteLine? Улучшено ли C#? –

+1

Функция C# 6, для этих экспериментов мне нравится использовать VS2015 :-) просто хороший способ записи string.Format() – Yoeri

+0

Я скажу! Больше не нужно перенумеровать элементы форматирования строки или помеченные заполнители формата! –

0

Очевидно, что я не знаю большую структуру вашего проекта, но вы можете просто сделать свой первый пример более общий характер, заменив Entities.Nutrient с TEntity? Это та вещь, я имею в виду:

public IEnumerable<TEntity> FindAllForSpecificLanguage<TEntity>(
    Func<DbContext, IEnumerable<TEntity>> selectEntities, 
    Func<TEntity, IEnumerable<object>> selectEntityDescriptions) 
{ 
    using (var context = CreateObjectContext()) 
    { 
     context.ContextOptions.LazyLoadingEnabled = false; 

     var list = selectEntities(context) 
      .Select(nut => new 
      { 
       Entity = nut, 
       Descriptions = selectEntityDescriptions 
      }).ToList(); //perform query 
     var resultList = list 
      .Select(entity => entity.Entity); 

     return resultList; 
    } 
} 
+0

Linq субъектам имеет ограниченную поддержку того, что он может перевести на SQL. Вызовы метода не допускаются. Я решил это вчера вечером, пример опубликован на моем github. – Yoeri

+0

Я отправил объяснение, если вам интересно :-) – Yoeri

+0

Если вы решили его, вы должны отправить ответ на свой вопрос. Однако в моем решении нет вызова метода, так что вы уверены, что он не сработает? –

Смежные вопросы