c# - una - generar orden de EF por expresión por cadena
string c# ejemplos (2)
Puede intentar convertir el método Generate
en un método genérico:
private Expression<Func<Task, TResult>> Generate<TResult>(string orderby)
{
switch (orderby)
{
case "Time":
return t => t.Time;
case "Money":
return t => t.RewardMoney;
default:
return t => t.Id;
}
}
Por lo tanto, si llama a este método, debe especificar el tipo de propiedad que desea ordenar de la siguiente manera:
_context.Items.OrderBy(Generate<decimal>("Money"));
Ahora recuerde que TResult
solo puede ser un tipo primitivo o un tipo de enumeración.
Quiero generar expresión por parámetro de cadena, algún código como:
private Expression<Func<Task, T>> Generate(string orderby)
{
switch (orderby)
{
case "Time":
return t => t.Time;
case "Money":
return t => t.RewardMoney;
default:
return t => t.Id;
}
}
entonces llámalo:
_context.Items.OrderBy(Generate("Money"));
¡Pero no puede compilar! Cambio T para objetar.
private Expression<Func<Task, object>> Generate(string orderby)
Entonces puede compilar, pero no funciona.
System.NotSupportedException: no se puede convertir el tipo ''System.Int32'' para escribir ''System.Object''. LINQ to Entities solo admite la conversión de primitiva EDM o tipos de enumeración.
Usando árboles de reflexión y expresión puede proporcionar los parámetros y luego llamar a la función OrderBy
, en lugar de devolver Expression<Func<Task, T>>
y luego llamar a OrderBy
.
Tenga en cuenta que OrderBy
es un método de extensión y se ha implementado en las clases System.Linq.Enumarable
y System.Linq.Queryable
. El primero es para linq-to-objects y el último es para linq-to-entities . entity-framework necesita el árbol de expresiones de la consulta para traducirlo a comandos SQL. Entonces usamos la implementación Queryable
.
Se puede hacer por un método de extensión (explicaciones agregadas como comentarios):
public static IOrderedQueryable<TSource> OrderBy<TSource>(
this IEnumerable<TSource> query, string propertyName)
{
var entityType = typeof(TSource);
//Create x=>x.PropName
var propertyInfo = entityType.GetProperty(propertyName);
ParameterExpression arg = Expression.Parameter(entityType, "x");
MemberExpression property = Expression.Property(arg, propertyName);
var selector = Expression.Lambda(property, new ParameterExpression[] { arg });
//Get System.Linq.Queryable.OrderBy() method.
var enumarableType = typeof(System.Linq.Queryable);
var method = enumarableType.GetMethods()
.Where(m => m.Name == "OrderBy" && m.IsGenericMethodDefinition)
.Where(m =>
{
var parameters = m.GetParameters().ToList();
//Put more restriction here to ensure selecting the right overload
return parameters.Count == 2;//overload that has 2 parameters
}).Single();
//The linq''s OrderBy<TSource, TKey> has two generic types, which provided here
MethodInfo genericMethod = method
.MakeGenericMethod(entityType, propertyInfo.PropertyType);
/*Call query.OrderBy(selector), with query and selector: x=> x.PropName
Note that we pass the selector as Expression to the method and we don''t compile it.
By doing so EF can extract "order by" columns and generate SQL for it.*/
var newQuery = (IOrderedQueryable<TSource>)genericMethod
.Invoke(genericMethod, new object[] { query, selector });
return newQuery;
}
Ahora puede llamar a esta sobrecarga de OrderBy
como cualquier otra sobrecarga.
Por ejemplo:
var cheapestItems = _context.Items.OrderBy("Money").Take(10).ToList();
Que se traduce a:
SELECT TOP (10) {coulmn names} FROM [dbo].[Items] AS [Extent1]
ORDER BY [Extent1].[Money] ASC
Este enfoque se puede usar para definir todas las sobrecargas de los métodos OrderBy
y OrderByDescending
para tener selector de propiedad de string
.