#c# #linq #entity-framework #lambda
#c# #linq #entity-framework #лямбда
Вопрос:
Интересно, как я могу хранить выражения orderby в списке. Это то, что я хотел написать:
List<Expression<Func<Products,Object>>> list = new List<Expression<Func<Products,Object>>>()
{
p => p.Name,
p => p.Id
};
Затем:
var expr = list[0];
myProducts.OrderBy( expr );
который работает для p.Name
, но не работает для p.Id
( list[1]
), поскольку он удаляет следующее исключение
Необработанное исключение типа ‘System.В EntityFramework.SQLServer произошло исключение NotSupportedException’.Дополнительная информация о dll: Не удалось преобразовать тип ‘System.Int32’ в тип ‘System.Объект’. LINQ для объектов поддерживает только приведение примитивов EDM или типов перечисления.
Какой тип списка я должен использовать?
Комментарии:
1. Обходной путь заключается в написании,
p => p.Id.ToString()
но я хотел бы, чтобы он также работал сSystem.Int32
2. @RobertFricke это не обходной путь, это также приведет к исключению NotSupportedException
3. @RobertFricke: Если вы упорядочите его как строку, тогда будет диапазон от 1 до 12
1, 10, 11, 12, 2, 3, 4, 5, 6, 7, 8, 9
, что не то, что вы хотите.4. Вместо Func<Продукт, объект> попробуйте Func<Продукт, динамический>
5. @RandRandom, потому что я пытался.
Ответ №1:
Вот мое решение (с использованием отражения и на основе идей DynamicLinq):
Определение ConvertableExpression
класса, чтобы мы могли перехватывать вызовы нашего пользовательского OrderBy()
:
public class ConvertableExpression<T>
{
public ConvertableExpression(Expression<Func<T, object>> expr)
{
this.Expression = expr;
}
public Expression<Func<T, object>> Expression { get; private set; }
}
Представляем метод расширения для упрощения приведения из обычного Expression
:
public static class ExpressionExtensions
{
public static ConvertableExpression<T> AsConvertable<T>(this Expression<Func<T, object>> expr)
{
return new ConvertableExpression<T>(expr);
}
}
Расширение IQueryable
с помощью реализации на основе отражения OrderBy()
:
public static class QueryableExtensions
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, ConvertableExpression<T> expr)
{
Expression queryExpr = source.Expression;
var exprBody = SkipConverts(expr.Expression.Body);
var lambda = Expression.Lambda(exprBody, expr.Expression.Parameters);
var quote = Expression.Quote(lambda);
queryExpr = Expression.Call(typeof(Queryable), "OrderBy", new[] { source.ElementType, exprBody.Type }, queryExpr, quote);
return (IOrderedQueryable<T>)source.Provider.CreateQuery(queryExpr);
}
private static Expression SkipConverts(Expression expression)
{
Expression result = expression;
while (result.NodeType == ExpressionType.Convert || result.NodeType == ExpressionType.ConvertChecked)
result = ((UnaryExpression)result).Operand;
return resu<
}
}
Использование:
myProducts.OrderBy(expr.AsConvertable());
Комментарии:
1. Почему «OrderBy» возвращается с IQueryable вместо ожидаемого IOrderedQueryable, как это делает «оригинальный» OrderBy? Должен ли я привести его к этому интерфейсу в конце?
2. Вы правы. Я изменил метод на return
IOrderedQueryable
.
Ответ №2:
попробуйте это
List<Func<Products, Object>> list = new List<Func<Products, Object>>()
{
new Func<Products,Object>( p => p.Name),
new Func<Products,Object>( p => p.Id),
};
Комментарии:
1. Теперь нам нужно только объяснение, почему это работает как
Expression
со строкой, но не с типом int32, и почему ваше понимание отличается внутренне2. Не вижу разницы между вашим решением и моим… какова основная точка зрения?
3. Это решение вызовет Linq для объектов (
IEnumerable.OrderBy
) вместо Linq для сущностей (IQueryable.OrderBy
) и не будет преобразовано в store-query (в данном случае SQL).
Ответ №3:
Таким образом, похоже, что реализация OrderBy для EF работает, проверяя, является ли < T > структурой или объектом, и поэтому вы указываете ему вызвать OrderBy<…, object >(someStructTypeVariable)
В качестве обходного пути я бы предложил вам хранить целые делегаты вместо выражений.
Попробуйте это:
internal static class MyExtensions
{
public static IOrderedQueryable<TSource> OrderBy<TSource, TField>(this IQueryable<TSource> source, Expression<Func<TSource, TField>> selector, bool descending)
{
return descending
? source.OrderByDescending(selector)
: source.OrderBy(selector);
}
}
var orderers = new List<Func<IQueryable<Products>, IOrderedQueryable<Products>>>()
{
source => source.OrderBy(x => x.Id, true),
source => source.OrderBy(x => x.Id, false),
source => source.OrderBy(x => x.Name, false)
};
// To be replaced with entity source-collection.
IQueryable<Products> dummySource = new EnumerableQuery<MyType>(new List<Products>());
orderers[0](dummySource.Where(x => x.Id != 0));
Комментарии:
1. Извините, для меня это не очень хорошее решение:( поскольку мои выражения могут использоваться либо в OrderBy, либо в OrderByDescending, ThenBy, ThenByDescending также.
2. Затем напишите метод-оболочку, который будет возвращать orderby или orderby по убыванию на основе дополнительного параметра. Проявите творческий подход.
3. Пытаюсь проявить творческий подход, но не могу понять, как ваше решение работает с методом-оболочкой — поскольку ваше решение напрямую вызывает OrderBy.
4. Я обновил пример для вас. Проверьте, работает ли это для вас. Но я действительно не знаю, зачем вы это используете. вы могли бы создать две коллекции: для возрастания и убывания.
5. Единственным другим решением было бы получить правильный метод путем отражения.