我试图以不同的方式实现OrderBy
和ThenBy
,以从OrderBy
和ThenBy
扩展方法中隐藏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
感谢您的帮助。
答案 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;
}
}
一切都会好起来的。