c# - LINQ to Entities 和 LINQ to Objects 处理类型转换的方式不同

标签 c# linq entity-framework linq-to-entities

我最近加入了一个项目,其中有一个 Sort方法有条件地将 lambda 表达式传递给 LINQ 查询,以定义应根据哪个属性进行排序。问题是在 Func<TEntity, Object 中传递了 lambda 表达式。 > 而不是在 Expression<Func<TEntity, Object>> 中这样排序是在内存中而不是在数据库中进行的(因为调用了需要 OrderByIEnumerable 的重载)。这是 SortWithDelegate 中的版本(见下文)。

当我使用 Expression<Func<TEntity, Object>> (请参阅下面的 SortWithExpression)然后,当在 orderBy 中传递字符串属性时参数,排序在数据库中正确完成。但是,当我尝试使用 Expression<Func<TEntity, Object>> 对整数(或日期时间)进行排序时我收到以下错误:

Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.

为了避免这种情况,我必须将要排序的整数或日期时间字段包装在匿名类型中,如下所示:orderByFunc = sl => new {sl.ParentUnit.Id}; .我知道我需要这样做作为 Func 的返回类型是Object .但是,我不明白的是为什么在使用 LINQ to Entities 提供程序而不是 LINQ to Objects 提供程序时需要这样做?

void Main()
{
    var _context = new MyContext();

    string sortProperty = "Id";
    bool sortAscending = false;


    IQueryable<Qualification> qualifications = _context.Qualifications.Include(q => q.ParentUnit);

    qualifications = SortWithExpression(sortProperty, sortAscending, qualifications);

    qualifications.Dump();

}

private static IQueryable<Qualification> SortWithDelegate(string orderBy, bool sortAscending, IQueryable<Qualification> qualificationsQuery)
{
    Func<Qualification, Object> orderByFunc;

    switch (orderBy)
    {
        case "Name":
            orderByFunc = sl => sl.Name;
            break;
        case "ParentUnit":
            orderByFunc = sl => sl.ParentUnit.Name;
            break;
        case "Id":
            orderByFunc = sl => sl.ParentUnit.Id;
            break;
        case "Created":
            orderByFunc = sl => sl.Created;
            break;
        default:
            orderByFunc = sl => sl.Name;
            break;
    }

    qualificationsQuery = sortAscending
        ? qualificationsQuery.OrderBy(orderByFunc).AsQueryable()
            : qualificationsQuery.OrderByDescending(orderByFunc).AsQueryable();

    return qualificationsQuery;
}

private static IQueryable<Qualification> SortWithExpression(string orderBy, bool sortAscending, IQueryable<Qualification> qualificationsQuery)
{
    Expression<Func<Qualification, Object>> orderByFunc;

    switch (orderBy)
    {
        case "Name":
            orderByFunc = sl => sl.Name;
            break;
        case "ParentUnit":
            orderByFunc = sl => sl.ParentUnit.Name;
            break;
        case "Id":
            orderByFunc = sl => new {sl.ParentUnit.Id};
            break;
        case "Created":
            orderByFunc = sl => new {sl.Created};
            break;
        default:
            orderByFunc = sl => sl.Name;
            break;
    }

    qualificationsQuery = sortAscending
        ? qualificationsQuery.OrderBy(orderByFunc)
            : qualificationsQuery.OrderByDescending(orderByFunc);

    return qualificationsQuery;
}

已添加

只是想我会添加我自己的解决方案来解决这个问题。避免装箱intdatetime我在 IQueryable<T> 上创建了一个通用扩展方法我将 lambda 表达式传递给它以指示排序字段和一个 bool 值以指示排序顺序是否应升序:

    public static IQueryable<TSource> OrderBy<TSource, TResult>(this IQueryable<TSource> query, Expression<Func<TSource, TResult>> func, bool sortAscending)
    {
        return sortAscending ?
            query.OrderBy(func) :
                query.OrderByDescending(func);
    }

    private static IQueryable<Qualification> Sort(string orderBy, bool sortAscending, IQueryable<Qualification> qualificationsQuery)
    {
        switch (orderBy)
        {
            case "Name":
                return qualificationsQuery.OrderBy(sl => sl.Name, sortAscending);
            case "ParentUnit":
                return qualificationsQuery.OrderBy(s1 => s1.ParentUnit.Name, sortAscending);
            default:
                return qualificationsQuery.OrderBy(sl => sl.Name, sortAscending);
        }
    }

最佳答案

顾名思义,表达式树是关于做某事的表达式。您可以访问表达式并将它们解释为您自己的业务,或者像 lambda 表达式一样,您可以编译它们并作为委托(delegate)调用。

当您将表达式传递给 Linq to Entities 中的 orderby 方法时,Linq to Entities 将访问它,在您的情况下,将生成“Int32 to Object”异常,因为它被解释为 MemberInfo 的方式变成数据库查询的列名。但是当您将它用作 Func 委托(delegate)时,它不能被翻译,它将作为委托(delegate)被调用以在 orderby 方法的排序算法中进行比较。

关于c# - LINQ to Entities 和 LINQ to Objects 处理类型转换的方式不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27605648/

相关文章:

c# - 没有名为 fcntl 的模块

c#:Enumerable 类中的 Where()、OrderBy() 和 Select() 不应该采用委托(delegate)类型、lambda 表达式或匿名类型作为参数

c# - 如何在 EF 4.1 中急切加载祖 parent 记录

c# - 允许用户访问[授权]页面 - MVC

c# - 如何将隐藏字段值从 View 传递到 Controller ASP.NET MVC 5?

entity-framework - 存储过程返回错误的标量值-1,而不是返回值

javascript - 在下拉列表中按选定区域限制搜索

c# - 带有 eventData 对象的 EventHubTrigger C# 不起作用

c# - 当 URL 中有尾随空格时 WebAPI 路由 404

c# - 如何在 LINQ 中使用表字段创建新的 DateTime where 条件