#c_sharp #entity_framework #linq #expressions
Пытаюсь сделать LINQ запрос в котором можно менять поле (одно) по которому нужно
производить сортировку. Использование переменной предиката позволяет это
public void GetMyData(string sortFieldName)
{
Func orderPredicate = null;
switch (sortFieldName)
{
case "FIELD_STRING":
orderPredicate = x => x.FIELD_STRING;
case "FIELD_INT":
orderPredicate = x => x.FIELD_INT;
case "FIELD_DATE":
orderPredicate = x => x.FIELD_DATE;
default:
orderPredicate = x => x.FIELD_DATE;
}
var queryResult = db.MyEntity.OrderBy(orderPredicate);
//...some logic
}
Но резко (в десятки раз) падает производительность по сравнению с прямым заданием
условия сортировки.
Насколько понимаю, должно помочь Expression. Меняю тип переменной
Expression> orderPredicate = null;
Но в рантайме выдаёт исключение
Не удалось привести тип "System.DateTime" к типу "System.Object". LINQ to Entities
поддерживает только приведение типов-примитивов моделей EDM или типов перечисления.
Как же быть? Ведь, если я буду использовать прямое задание условия
public void GetMyData(string sortFieldName)
{
var query = db.MyEntity;
var orderedQuery = query.OrderBy(x => x.FIELD_DATE);
switch (sortFieldName)
{
case "FIELD_STRING":
orderedQuery = query.OrderBy(x => x.FIELD_STRING); break;
case "FIELD_INT":
orderedQuery = query.OrderBy(x => x.FIELD_INT); break;
case "FIELD_DATE":
orderedQuery = query.OrderBy(x => x.FIELD_DATE); break;
}
//...some logic
}
, мне будет трудно усложнить запрос дополнительными инструкциями Where, Include и т.д.
Ответы
Ответ 1
Проблема в том, что когда вы пишите вот так: Expression> orderPredicate; orderPredicate = x => x.FIELD_DATE; Компилятор делает вот так: var _param = Expression.Parameter(typeof(MyEntity), "x"); orderPredicate = Expression.Lambda >( Expression.Convert( Expression.Property(_param, "FIELD_DATE"), typeof(object) ), _param ); Вот на этот самый Expression.Convert EF и ругается. Для того чтобы не было преобразования - возвращаемый тип делегата обязан совпадать с типом свойства, что в свою очередь означает что общую переменную orderPredicate вы использовать не можете. Теперь как строить такие запросы. А строятся они очень просто - через накопление IQueryable: IQueryable q = db.MyEntity; switch (sortFieldName) { case "FIELD_STRING": q = q.OrderBy(x => x.FIELD_STRING); case "FIELD_INT": q = q.OrderBy(x => x.FIELD_INT); case "FIELD_DATE": q = q.OrderBy(x => x.FIELD_DATE); default: q = q.OrderBy(x => x.FIELD_DATE); } Никакого усложнения тут нет, полученный запрос можно точно также дополнять другими условиями: q = q.Where(x => x.Foo > 42); PS поскольку у вас в параметре sortFieldName передается имя свойства, построение выражения можно еще немного упростить. Да, класс Queryable не дает нам вызвать OrderBy с неизвестным заранее именем свойства - но никто не мешает "раскрыть" этот метод и работать непосредственно с IQueryable: IQueryable q = db.MyEntity; var entityParam = Expression.Parameter(typeof(MyEntity)); var propExpr = Expression.Property(entityParam, sortFieldName) q = q.Provider.CreateQuery ( Expression.Call(typeof(Queryable), "OrderBy", new [] { entityParam.Type, propExpr.Type }, // Типы-параметры для метода Queryable.OrderBy<,> q.Expression, Expression.Quote(Expression.Lambda(propExpr, entityParam)) ) ); Также вместо того чтобы делать это вручную - можно использовать библиотеку System.Linq.Dynamic, скачав ее из nuget: q = q.OrderBy(sortFieldName);
Комментариев нет:
Отправить комментарий