问题描述
我有自定义的OrderBy实现,它仅适用于没有继承的类型,如果我想按基本类型的字段进行排序,则无法翻译LINQ表达式
I have custom OrderBy implementation, it only works for types without inheritance, if I want order by field from base type I got The LINQ expression could not be translated
public static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (string.IsNullOrEmpty(orderByProperty))
{
throw new ArgumentNullException(nameof(orderByProperty));
}
var command = desc ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var param = Expression.Parameter(type, "p");
var property = type.GetProperty(orderByProperty);
var propertyAccess = Expression.MakeMemberAccess(param, property);
var orderByExpression = Expression.Lambda(propertyAccess, param);
var resultExpression = Expression.Call(
typeof(Queryable),
command,
new Type[] { type, property.PropertyType },
source.Expression,
Expression.Quote(orderByExpression));
return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery(resultExpression);
}
我使用的是EntityFramework Core 2.2,但非常有趣的想法是,如果我写只需 source.OrderBy(x => x.someBaseField)
,它就可以正常工作,因此必须通过我的自定义实现来实现
I am using entityframework core 2.2 but the very interesting thigs is that if I write just source.OrderBy(x=>x.someBaseField)
then it works without any problem, so there must by something with my custom implementation
在错误日志中,我也得到了翻译后的查询,看起来像这样,有趣的是结尾部分
In error log I got also the translated query and it looks like this, intereresting is end part
orderby new SomeType() {NewField = [entity].DbField, Id = [entity].Id}.Id desc
orderByExpression.Body {p => p.Id}
resultExpression
.Call System.Linq.Queryable.OrderByDescending(
.Call System.Linq.Queryable.Select(
.Call System.Linq.Queryable.Where(
.Call System.Linq.Queryable.Where(
.Constant<Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MyTypeView]>(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[MyTypeView]),
'(.Lambda #Lambda1<System.Func`2[MyTypeView,System.Boolean]>)),
'(.Lambda #Lambda2<System.Func`2[MyTypeView,System.Boolean]>)),
'(.Lambda #Lambda3<System.Func`2[MyTypeView, MyTypeResult]>))
,
'(.Lambda #Lambda4<System.Func`2[MyTypeResult,System.Guid]>))
推荐答案
我以前见过类似的东西。编译器生成和手动表达式之间的唯一区别是 PropertyInfo
的 ReflectedType
属性-在编译器生成的代码中相同为 DeclaringType
,在本例中为基类,而在通过获得的
是用于获取它的派生类型。 PropertyInfo
中type.GetProperty
I've seen something like this before. The only difference between compiler generated and manual expression is the ReflectedType
property of the PropertyInfo
- in compiler generated code it's the same as DeclaringType
, which in this case is the base class, while in the PropertyInfo
obtained via type.GetProperty
it is the derived type used to obtain it.
由于某些未知原因(可能是错误),这使EF Core感到困惑。解决方法是更改代码,如下所示:
For some unknown reason (probably a bug) this is confusing EF Core. The workaround is to change the code as follows:
var property = type.GetProperty(orderByProperty);
if (property.DeclaringType != property.ReflectedType)
property = property.DeclaringType.GetProperty(property.Name);
或使用类似这样的帮助方法
or use a helper method like this
static PropertyInfo GetProperty(Type type, string name)
{
for (; type != null; type = type.BaseType)
{
var property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
if (property != null) return property;
}
return null;
}
为了支持嵌套属性,我将添加以下助手
In order to support nested properties, I would add the following helpers
static Expression Property(Expression target, string name) =>
name.Split('.').Aggregate(target, SimpleProperty);
static Expression SimpleProperty(Expression target, string name) =>
Expression.MakeMemberAccess(target, GetProperty(target.Type, name));
然后使用
var propertyAccess = Property(param, orderByProperty);
和
new Type[] { type, orderByExpression.ReturnType },
这篇关于LINQ表达式无法转换为基本属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!