在SQL树错误中重新实现OrderBy,ThenBy和Null TypeMapping

时间:2019-07-18 08:15:02

标签: c# linq lambda entity-framework-core expression-trees

我试图以不同的方式实现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:SQL树中的Null TypeMapping   Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.RelationalSqlTranslatingExpressionVisitor + SqlTypeMappingVerifyingExpressionVisitor.VisitExtension(Expression   节点)

源代码可用here

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

首先,您正在使用预览(测试版)软件,该软件可能会出现问题。

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

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

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

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

并将抽象方法签名更改为

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

实现,接口以及除具体类之外的所有其他内容将保持不变。

现在,您所需要做的就是在继承的类中指定实际的密钥类型,并更改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;
    }
}

一切都会好起来的。