#c_sharp #entity_framework #linq #expressions
Пытаюсь сделать LINQ запрос в котором можно менять поле (одно) по которому нужно производить сортировку. Использование переменной предиката позволяет это public void GetMyData(string sortFieldName) { FuncorderPredicate = 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);
Комментариев нет:
Отправить комментарий