C# 如何指定谓词';s型,我赢了';直到运行时我才知道?

C# 如何指定谓词';s型,我赢了';直到运行时我才知道?,c#,entity-framework,linq,lambda,C#,Entity Framework,Linq,Lambda,我的存储库方法从数据库中获取内容。它接受排序顺序作为参数: IEnumerable<Car> getCars<TSortKey>(Expression<Func<Car, TSortKey>> sort); 我在谓词中使用object,因为直到运行时我才知道类型。但这会生成错误的SQL,并抛出 那么我该如何解决这个问题呢?问题是表达式为值类型属性生成额外的转换,EF不喜欢这些属性,并抛出NotSupportedException 您可以在存储库类

我的存储库方法从数据库中获取内容。它接受排序顺序作为参数:

IEnumerable<Car> getCars<TSortKey>(Expression<Func<Car, TSortKey>> sort);
我在谓词中使用
object
,因为直到运行时我才知道类型。但这会生成错误的SQL,并抛出


那么我该如何解决这个问题呢?

问题是
表达式
为值类型属性生成额外的
转换
,EF不喜欢这些属性,并抛出
NotSupportedException

您可以在存储库类中使用以下helper方法,而不是
OrderBy
。如果需要,它将剥离
Convert
表达式,并动态调用
Queryable.OrderBy
方法:

public static partial class EFExtensions
{
    public static IOrderedQueryable<T> SortBy<T>(this IQueryable<T> source, Expression<Func<T, object>> keySelector)
    {
        var body = keySelector.Body;
        if (body.NodeType == ExpressionType.Convert)
            body = ((UnaryExpression)keySelector.Body).Operand;
        var selector = Expression.Lambda(body, keySelector.Parameters);
        var orderByCall = Expression.Call(
            typeof(Queryable), "OrderBy", new[] { typeof(T), body.Type },
            source.Expression, Expression.Quote(selector));
        return (IOrderedQueryable<T>)source.Provider.CreateQuery(orderByCall);
    }
}
公共静态部分类扩展
{
公共静态IOrderedQueryable排序方式(此IQueryable源、表达式键选择器)
{
var body=keySelector.body;
if(body.NodeType==ExpressionType.Convert)
body=((UnaryExpression)keySelector.body).操作数;
var selector=Expression.Lambda(body,keySelector.Parameters);
var orderByCall=Expression.Call(
typeof(Queryable),“OrderBy”,new[]{typeof(T),body.Type},
source.Expression,Expression.Quote(选择器));
返回(IOrderedQueryable)source.Provider.CreateQuery(orderByCall);
}
}

可能的解决方案:我可以将方法调用移动到每个开关案例中,但这会变得非常混乱。e、 g.
case-SortOption.Name:cars=repo.getCars(x=>x.Name);中断我相当肯定您可能的解决方案是最好的。另一种可能是将您的
排序
枚举传递到repo中,并在适当的位置构建正确的lamda。@Jamiec是的,我现在正在用“混乱”的方式,但它变得非常混乱,因为这只是我真实代码的一小部分。我希望我能以某种方式重写lambda,改为使用泛型类型。您可以考虑使用反射来创建表达式函数,创建getCars的泛型方法并调用它。如果你需要更多的帮助,我可以写一个答案怎么办。这真的很棒。我还添加了一个
SortByDescending
方法。像这样重写表达式和反射一样“慢”吗?我认为不是,因为您不是在查询类型和元数据,而是在创建新的内容。是吗?我相信你能做到!尽管您也可以将
bool descending
参数添加到上述方法中,并在内部使用
“OrderBy”+(descending?“descending”:“)
),但在这种情况下重写lambda与反射不同,因为您是在重用已构建的表达式(body或body.operator)。关于
表达式.Call
,这正是
Queryable
方法所做的-是的,这很有意义。再次感谢。
public static partial class EFExtensions
{
    public static IOrderedQueryable<T> SortBy<T>(this IQueryable<T> source, Expression<Func<T, object>> keySelector)
    {
        var body = keySelector.Body;
        if (body.NodeType == ExpressionType.Convert)
            body = ((UnaryExpression)keySelector.Body).Operand;
        var selector = Expression.Lambda(body, keySelector.Parameters);
        var orderByCall = Expression.Call(
            typeof(Queryable), "OrderBy", new[] { typeof(T), body.Type },
            source.Expression, Expression.Quote(selector));
        return (IOrderedQueryable<T>)source.Provider.CreateQuery(orderByCall);
    }
}