在 Sql 树错误中重新实现 OrderBy、ThenBy 和 Null TypeMapping

Re-implementation of OrderBy, ThenBy and Null TypeMapping in Sql Tree error

我正在尝试以不同的方式实现 OrderByThenBy,以从 OrderByThenBy 扩展方法中隐藏 lambda 表达式。这些扩展方法接受实现 IOrderSpecification 的 类:

public class PersonOrderByAgeSpecification : OrderSpecification<Person>
{
    public PersonOrderByAgeSpecification(Sort direction= Sort.Ascending) : base(direction)
    {
    }

    public override Expression<Func<Person, IComparable>> AsExpression()
    {
        return personOrder => personOrder.Age;
    }
}

以及用法:

 var orderSpecification = new PersonOrderByAgeSpecification(Sort.Ascending); 
 var sortedPeople=  _dbContext.People.OrderBy(orderSpecification);

AsExpression() 中的 属性 类型只是字符串时,它工作正常。例如:

public override Expression<Func<Person, IComparable>> AsExpression()
{
    return personOrder => personOrder.FirstName;
}

否则我会得到这个错误:(不适用于整数或布尔)

InvalidOperationException: Null TypeMapping in Sql Tree Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.RelationalSqlTranslatingExpressionVisitor+SqlTypeMappingVerifyingExpressionVisitor.VisitExtension(Expression node)

源代码可用here

感谢任何帮助。

首先,您使用的是预览版(测试版)软件,预计会出现问题。

但主要问题是 LINQ 排序方法有第二个泛型类型参数 TKey,您将其隐藏在 IComparable 后面,这对于值类型会导致表达式内部的隐藏转换。

除了不必要的装箱之外,这对于 LINQ to Objects 提供程序来说不是问题,因为它只是编译并执行来自 lambda 表达式的委托。然而,其他 IQueryable 提供商通常需要将表达式转换为其他内容(通常是 SQL)。他们中的大多数人识别出这样的转换(Expression.Convert)并在处理过程中将其删除。显然您使用的 EF 3.0 预览版没有,因此例外。

您可以通过自己消除隐藏的转换来避免此类问题。可以通过表达式操作来做到这一点,但最简单的方法是将第二个泛型类型参数引入基本抽象 class:

public abstract class OrderSpecification<T, TKey> : IOrderSpecification<T>

并将抽象方法签名更改为

public abstract Expression<Func<T, TKey>> AsExpression();

实现、接口和除具体 classes 之外的所有其他内容将保持原样。

现在您只需在继承的 class 中指定实际密钥类型并更改 AsExpression 覆盖签名。例如:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

public class PersonAgeOrderSpecification : OrderSpecification<Person, int>
{
    public PersonAgeOrderSpecification(Sort direction) : base(direction) { }
    public override Expression<Func<Person, int>> AsExpression()
    {
        return person => person.Age;
    }
}

一切都会好起来的。