Comment appliquer OrderBy sur IQueryable en utilisant un nom de colonne de chaîne dans une méthode d'extension générique?

.net c# entity-framework expression-trees linq

Question

public static IQueryable<TResult> ApplySortFilter<T, TResult>(this IQueryable<T> query, string columnName)
  where T : EntityObject
{
  var param = Expression.Parameter(typeof(T), "o");
  var body = Expression.PropertyOrField(param,columnName);

  var sortExpression = Expression.Lambda(body, param);
  return query.OrderBy(sortExpression);
}

Parce que le type de OrderBy n'est pas déduit de sortExpression, je dois le spécifier comme ceci au moment de l'exécution:

public static IQueryable<TResult> ApplySortFilter<T, TResult>(this IQueryable<T> query, string columnName)
  where T : EntityObject
{
  var param = Expression.Parameter(typeof(T), "o");
  var body = Expression.PropertyOrField(param,columnName);

  var sortExpression = Expression.Lambda(body, param);
  return query.OrderBy(sortExpression);
}

Ou

public static IQueryable<TResult> ApplySortFilter<T, TResult>(this IQueryable<T> query, string columnName)
  where T : EntityObject
{
  var param = Expression.Parameter(typeof(T), "o");
  var body = Expression.PropertyOrField(param,columnName);

  var sortExpression = Expression.Lambda(body, param);
  return query.OrderBy(sortExpression);
}

Je ne pense pas que cela soit possible cependant, car TSortColumn ne peut être déterminé que pendant l'exécution.

Y a-t-il un moyen de contourner ceci?

Réponse acceptée

Nous avons fait quelque chose de similaire (pas à 100%, mais similaire) dans un projet LINQ to SQL. Voici le code:

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) {
    var type = typeof(T);
    var property = type.GetProperty(ordering);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExp = Expression.Lambda(propertyAccess, parameter);
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
    return source.Provider.CreateQuery<T>(resultExp);
}

Nous n’avions pas réellement utilisé un générique, nous avions une classe connue, mais cela devrait fonctionner sur un générique (j’ai mis l’espace générique générique à la place.

Edit: Pour un ordre décroissant, passez OrderByDescending au lieu de "OrderBy":

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) {
    var type = typeof(T);
    var property = type.GetProperty(ordering);
    var parameter = Expression.Parameter(type, "p");
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
    var orderByExp = Expression.Lambda(propertyAccess, parameter);
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
    return source.Provider.CreateQuery<T>(resultExp);
}

Réponse populaire

Vous pouvez également utiliser Dynamic Linq

Infos ici http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

C # télécharger ici http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx

Ensuite, ajoutez simplement le paramètre using Linq.Dynamic; et vous obtenez automatiquement 2 méthodes d'extension supplémentaires qui peuvent être utilisées comme ceci

return query.OrderBy("StringColumnName");



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi