Ich habe eine benutzerdefinierte OrderBy-Implementierung, die nur für Typen ohne Vererbung funktioniert, wenn ich die Reihenfolge nach Feld vom Basistyp erhalten möchte. Der LINQ-Ausdruck konnte nicht übersetzt werden
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);
}
Ich verwende Entityframework Core 2.2, aber das sehr interessante ist, dass wenn ich nur source.OrderBy(x=>x.someBaseField)
schreibe, es ohne Probleme funktioniert, also muss es etwas mit meiner benutzerdefinierten Implementierung geben
Im Fehlerprotokoll habe ich auch die übersetzte Abfrage bekommen und es sieht so aus, interessant ist Endteil
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]>))
Ich habe so etwas schon mal gesehen. Der einzige Unterschied zwischen dem vom Compiler generierten und dem manuellen Ausdruck besteht in der ReflectedType
Eigenschaft der PropertyInfo
Im vom Compiler generierten Code entspricht dies der DeclaringType
Eigenschaft, die in diesem Fall die Basisklasse ist, während es sich bei der über type.GetProperty
erhaltenen PropertyInfo
um den abgeleiteten Typ handelt verwendet, um es zu erhalten.
Aus irgendeinem unbekannten Grund (wahrscheinlich ein Fehler) ist dies verwirrend für EF Core. Die Problemumgehung besteht darin, den Code wie folgt zu ändern:
var property = type.GetProperty(orderByProperty);
if (property.DeclaringType != property.ReflectedType)
property = property.DeclaringType.GetProperty(property.Name);
oder verwenden Sie eine Hilfsmethode wie diese
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;
}
Um verschachtelte Eigenschaften zu unterstützen, würde ich die folgenden Helfer hinzufügen
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));
und dann verwenden
var propertyAccess = Property(param, orderByProperty);
und
new Type[] { type, orderByExpression.ReturnType },
innerhalb der fraglichen Methode.