Korzystanie reflection i expression-trees można podać parametry, a następnie zadzwonić OrderBy
funkcję, zamiast wracać Expression<Func<Task, T>>
a następnie wywołanie OrderBy
.
Należy pamiętać, że OrderBy
jest metodą rozszerzenia i zaimplementowano w klasach System.Linq.Enumarable
i System.Linq.Queryable
. Pierwszy z nich to linq-to-objects, a drugi dotyczy linq-to-entities. entity-framework potrzebuje drzewa wyrażeń zapytania, aby przetłumaczyć je na polecenia SQL. Dlatego używamy implementacji Queryable
.
Można to zrobić za pomocą metody wydłużania (objaśnienia dodanych komentarzach):
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;
}
Teraz można nazwać to przeciążenie OrderBy
jak każdy inny przeciążenie niego.
Na przykład:
var cheapestItems = _context.Items.OrderBy("Money").Take(10).ToList();
co przekłada się na:
SELECT TOP (10) {coulmn names} FROM [dbo].[Items] AS [Extent1]
ORDER BY [Extent1].[Money] ASC
Takie podejście może być wykorzystane do określenia wszystkich przeciążeniem OrderBy
i OrderByDescending
metod mieć string
selektor nieruchomości.
Możliwy duplikat [Jak określić dynamicznie argument LinB OrderBy?] (Http://stackoverflow.com/questions/7265186/how-do-i-specify-the-linq-orderby-argument-dynamicznie) –