Entity Framework OrderBy на поле из проекции

Я хочу отсортировать поле, известное только во время выполнения, и это поле может быть создано внутри select пункт.

Требования:

  1. Пользователь должен иметь возможность сортировки по любому столбцу;
  2. Этот столбец может принадлежать объекту или классу проекции;
  3. Программист решает, хотите ли вы вернуть ту же исходную сущность или проекцию.

Из-за (3) простая реализация не найдет поле из класса проекции, так как у меня есть статический тип типа базовый / сущность. Затем я начал менять код, чтобы рассмотреть полученный тип.

У меня такое чувство, что мешает просто статическая типизация, что на самом деле не помогает. к несчастью IQueryable<dynamic> не работает

Пример кода (детали опущены для краткости):

void Main()
{
    using (var db = new MyContext())
    {
        var original = db.Set<Sample>();

        // requirement (3), static type Sample, dynamic type SampleDTO
        Expression<Func<Sample, Sample>> fn = item => new SampleDTO() { Description = "Value: " + item.Name};
        var projected = original.Select(fn);

        // requirements (1) and (2)
        var ordered = OrderBy_Default<Sample, SampleDTO>(projected, "Description");
        ordered.Dump();
    }
}

private IQueryable<T> OrderBy_Default<T, TProject>(IQueryable<T> source, string ordering)
{
  var type = source.ElementType;
  var resultType = type;
  var property = resultType.GetProperty(ordering, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
  var parameter = Expression.Parameter(resultType, "p");
  var propertyAccess = Expression.MakeMemberAccess(parameter, property);
  var orderByExp = Expression.Lambda(propertyAccess, parameter);
  MethodCallExpression resultExp = Expression.Call(typeof(Queryable),
      "OrderBy",
      new Type[] { type, property.PropertyType },
      source.Expression, Expression.Quote(orderByExp));
  return source.Provider.CreateQuery<T>(resultExp);
}

Предыдущий код выдает эту ошибку:

Универсальный метод OrderBy типа Queryable не совместим с предоставленными аргументами и аргументами типа. Аргументы типа не должны предоставляться, если метод не является универсальным.

я пытался projected.Cast<SampleDTO>():

Невозможно привести тип 'Sample' к типу 'UserQuery+SampleDTO'. LINQ to Entities поддерживает только приведение типов примитивов и перечислений EDM.

я пытался projected.OfType<SampleDTO>():

UserQuery + SampleDTO не является допустимым типом метаданных для операций фильтрации типов. Фильтрация типов действительна только для типов сущностей и сложных типов.

Cast() а также OfType() работает с L2O, но не с L2EF.

Добавление SampleDTO для модели сущности, конечно, это не ответ (хотя попробовал, ради того, чтобы знать, как это будет работать).

Мои сущности и прогнозы:

public class Sample
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class SampleDTO: Sample
{
    public string Description { get; set; }
}

Я сделал SampleDTO наследовать от Sample, это помогает. Это не требование, это вопрос удобства.

Некоторый возможный ответ:

Вместо Expression<Func<Sample, Sample>> я мог бы иметь Expression<Func<Sample, SampleDTO>>, Это лишает законной силы требование (3) и разрушило бы большую часть моего существующего кода.

PS: я собираюсь использовать библиотеку Microsoft Dynamic LINQ, но ради ответа я с нетерпением жду возможности узнать, что на самом деле происходит.

1 ответ

Это сработало для меня, если я правильно понимаю ваш вопрос, но у меня возникает ощущение, что я что-то упустил

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering)
    {
        var type = typeof(T);
        var property = type.GetProperty(ordering);
        var parameter = Expression.Parameter(type);
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByExp = Expression.Lambda(propertyAccess, parameter);
        MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
        return source.Provider.CreateQuery<T>(resultExp);
    }
Другие вопросы по тегам