在 Sql 树错误中重新实现 OrderBy、ThenBy 和 Null TypeMapping
Re-implementation of OrderBy, ThenBy and Null TypeMapping in Sql Tree error
我正在尝试以不同的方式实现 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: 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;
}
}
一切都会好起来的。
我正在尝试以不同的方式实现 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: 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;
}
}
一切都会好起来的。