Entity Framework OrderBy на поле из проекции
Я хочу отсортировать поле, известное только во время выполнения, и это поле может быть создано внутри select
пункт.
Требования:
- Пользователь должен иметь возможность сортировки по любому столбцу;
- Этот столбец может принадлежать объекту или классу проекции;
- Программист решает, хотите ли вы вернуть ту же исходную сущность или проекцию.
Из-за (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);
}