Squeak.ru - шаблоны программирования

С# порядок списка родительских объектов по дочернему свойству через отражение

У меня есть метод, который упорядочивает запрашиваемое свойство через отражение, например:

   public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
   {
       string command = desc ? "OrderByDescending" : "OrderBy";
       var parts = orderByProperty.Split('.');
       var parameter = Expression.Parameter(typeof(TEntity), "p");
       Expression body = parameter;
       if (parts.Length > 1)
       {
           for (var i = 0; i < parts.Length - 1; i++)
           {
               body = Expression.Property(body, parts[i]);
           }
       }
       var property = body.Type.GetProperty(parts.LastOrDefault())
       body = Expression.Property(body, property.Name);
       var orderByExpression = Expression.Lambda(body, parameter);
       resultExpression = Expression.Call(typeof(Queryable), command, 
                                       new Type[] { typeof(TEntity), property.PropertyType },
                                       source.Expression, Expression.Quote(orderByExpression));
       return source.Provider.CreateQuery<TEntity>(resultExpression);
   }

И это работает, если я использую его для свойств объекта или его дочерних свойств, но это не работает, когда дочерним свойством является список.

    // Success
    db.Users.OrderBy("Name", true)
    db.Users.OrderBy("CurrentAddress.City.Name", false)

    // Fail
    db.Users.OrderBy("PhoneLines.RenewalDate", true)

Моя модель такая:

    public class User {
        public string Name { get; set; }
        public Address CurrentAddress{ get; set; }
        public List<Phone> PhoneLines { get; set; }
    }

И я пытаюсь сделать что-то вроде этой работы:

    db.Users.OrderByDescending(x => 
        x.PhoneLines
          .OrderByDescending(y => y.RenewalDate)
          .Select(y => y.RenewalDate).FirstOrDefault()
    );

Прямо сейчас я заинтересован только в том, чтобы эта работа работала для дочерних элементов списка первого уровня, то есть меня не беспокоит сценарий каскадного списка, только один дочерний элемент в цепочке является списком.

Пока мои попытки не увенчались успехом. Я проверяю, является ли последнее свойство коллекцией, и если да, то я попытался создать лямбду для списка и другую лямбду для свойства целевого порядка элемента класса.


if (body.Type.Namespace.Equals("System.Collections.Generic"))
{
 var mainType = body.Type.GenericTypeArguments.FirstOrDefault();
 var parameterInner = Expression.Parameter(mainType, "x");
 var mainProperty = Expression.Property(parameterInner, property.Name);
 var orderByExpressionInner = Expression.Lambda(mainProperty, parameterInner);
 var orderByExpression = Expression.Lambda(body, parameter);

 var selM = typeof(Enumerable).GetMethods()
             .Where(x => x.Name == command)
             .First().MakeGenericMethod(mainType, property.PropertyType);

 var resultExpressionOuter = Expression.Call(typeof(Queryable), 
                            command, new Type[] { typeof(TEntity), body.Type },
                            source.Expression, 
                            Expression.Call(null, selM, parameterInner, orderByExpressionInner
                        ));
}

Мои знания с рефлексией ограничены, можно ли добиться этого с помощью рефлексии?

22.05.2019

  • So far my attempts have failed можно поподробнее? Что происходит и чего вы ожидаете вместо этого? 22.05.2019
  • Последняя попытка приводит к следующему исключению: Expression of type 'Phone' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable'1[Phone]' of method 'System.Linq.IOrderedEnumerable'1[Phone] OrderBy[Phone,System.DateTime](System.Collections.Generic.IEnumerable'1[Phone], System.Func'2[Phone,System.DateTime])' Я ожидаю, что выражение успешно упорядочивает набор данных EF с source.Provider.CreateQuery<TEntity>(resultExpression) 22.05.2019

Ответы:


1

Один из вариантов — поместить новое свойство в родительский класс и возложить на него ответственность за возврат требуемого значения. Такие, как это;

public class User
{
    public string Name { get; set; }
    public Address CurrentAddress { get; set; }
    public List<Phone> PhoneLines { get; set; }

    private DateTime _dummyValue;
    public DateTime GetRenewalDate
    {
        get
        {
            if (this.PhoneLines == null || this.PhoneLines.Count == 0)
                return DateTime.MinValue;
            else
            {
                return this.PhoneLines.OrderByDescending(y => y.RenewalDate).Select(y => y.RenewalDate).FirstOrDefault();
            }
        }
        set
        {
             _dummyValue = value;
        }
    }
 }

Убедитесь, что у вас есть обработка по умолчанию, если ваш список пуст или равен нулю, здесь я возвращаю MinValue of DateTime.

Изменить: чтобы обойти другую проблему, которую вы указали в комментариях.

Член указанного типа GetRenewalDate не поддерживается в LINQ to Entities. Поддерживаются только инициализаторы, члены сущностей и свойства навигации сущностей.

Вы можете попробовать добавить фиктивный набор, это также может работать как обходной путь (но не элегантно). Я отредактировал код выше, чтобы показать это

22.05.2019
  • Это умное решение, но оно выбрасывает 'The specified type member 'GetRenewalDate' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.' 22.05.2019
  • Ох, хорошо! Грязным решением является преобразование в список. См. второй ответ здесь: заголовок stackoverflow.com/questions/11584660/ 22.05.2019
  • @BrunoDiGiuseppe Я обновил решение для использования фиктивного набора, иногда это работает, но очень грязно! 23.05.2019
  • Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...