Ho un'implementazione personalizzata di OrderBy, funziona solo per i tipi senza ereditarietà, se voglio l'ordine per campo dal tipo di base ho ottenuto L'espressione LINQ non può essere tradotta
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);
}
Sto usando entityframework core 2.2 ma la cosa molto interessante è che se scrivo solo source.OrderBy(x=>x.someBaseField)
allora funziona senza problemi, quindi ci deve essere qualcosa con la mia implementazione personalizzata
Nel registro degli errori ho anche ottenuto la query tradotta e sembra così, l'intereresting è la parte finale
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]>))
Ho visto qualcosa di simile prima. L'unica differenza tra il compilatore generato e l'espressione manuale è la proprietà ReflectedType
di PropertyInfo
: nel codice generato dal compilatore è uguale a DeclaringType
, che in questo caso è la classe base, mentre in PropertyInfo
ottenuto tramite type.GetProperty
è il tipo derivato usato per ottenerlo.
Per qualche ragione sconosciuta (probabilmente un bug) questo confonde EF Core. La soluzione alternativa consiste nel modificare il codice come segue:
var property = type.GetProperty(orderByProperty);
if (property.DeclaringType != property.ReflectedType)
property = property.DeclaringType.GetProperty(property.Name);
o utilizzare un metodo di supporto come questo
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;
}
Per supportare le proprietà nidificate, aggiungerei i seguenti helper
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));
e quindi usare
var propertyAccess = Property(param, orderByProperty);
e
new Type[] { type, orderByExpression.ReturnType },
all'interno del metodo in questione.