LINQ to Entity 和 LINQ to Objects 处理强制转换的方式的差异

本文关键字:LINQ to 方式 转换 Entity Objects 处理 | 更新日期: 2023-09-27 18:35:03

我最近加入了一个项目,其中Sort方法有条件地将lambda表达式传递给LINQ查询,以定义应排序的属性。问题在于 lambda 表达式是在Func<TEntity, Object>而不是Expression<Func<TEntity, Object>>中传递的,因此排序是在内存中而不是在数据库中进行的(因为调用了需要IEnumerableOrderBy重载(。这是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 表达式以指示排序字段和一个布尔值,指示排序顺序是否应升序:

    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);
        }
    }

LINQ to Entity 和 LINQ to Objects 处理强制转换的方式的差异

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

当您在 Linq 到实体中将表达式传递给 orderby 方法时,Linq 将访问它,在您的情况下,将生成"Int32 到对象"异常,因为它被解释为 MemberInfo 的方式变成了数据库查询的列名。但是,当您将其用作 Func 委托时,它无法翻译,它将作为委托调用,以便在 orderby 方法的排序算法中进行比较。