2008-11-21 9 views
75
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); 
} 

Da der Typ für OrderBy nicht aus SortExpression geschlossen wird muss ich es so zur Laufzeit etwas spezifizieren:Wie verwende ich OrderBy auf einem IQueryable mit einem String-Spaltennamen innerhalb einer generischen Erweiterungsmethode?

var sortExpression = Expression.Lambda<T, TSortColumn>(body, param); 

Oder

return query.OrderBy<T, TSortColumn>(sortExpression); 

Ich glaube nicht, dass dies möglich ist, TSortColumn kann jedoch nur zur Laufzeit ermittelt werden.

Gibt es einen Weg um dies?

+0

Nicht sicher, ob [diese] (http://stackoverflow.com/a/12920204/1139347) wonach du suchst, aber sieh es dir an. Prost – joaopintocruz

+0

@JTew Wie kann ich eine zweite Bestellung durch Klausel..Say Orderby ID dann durch Datum – SRJ

Antwort

100

Wir haben etwas ähnliches (nicht 100% gleich, aber ähnlich) in einem LINQ to SQL-Projekt. Hier ist der 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); 
} 

Wir haben nicht wirklich ein generisches verwenden wir eine bekannte Klasse hatten, aber es sollte auf einem generische Arbeit (Ich habe die allgemeinen Platzhalter setzen, wo es sein soll).

Edit: Für absteigender Reihenfolge, passieren in OrderByDescending statt "OrderBy":

MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp)); 
+0

Heh no prob, kann ich die Antwort auf mich sowieso nicht zuweisen :) – JTew

+1

für absteigende Reihenfolge, in "OrderByDescending" statt "OrderBy" MethodCallExpression resultExp = Ausdruck.Call (typeof (Queryable), "OrderByDescending ", ... –

+3

Dies funktionierte gut, aber das folgende war nur ein wirklich schönes, sauberes Codebeispiel: http://stackoverflow.com/questions/41244/dynamic-linq-orderby – BenSwayne

6

Es scheint, dass this die Art und Weise, es zu tun, jetzt, zu überprüfen:

// ***** OrderBy(company => company) ***** 
// Create an expression tree that represents the expression 
// 'whereCallExpression.OrderBy(company => company)' 
MethodCallExpression orderByCallExpression = Expression.Call(
    typeof(Queryable), 
    "OrderBy", 
    new Type[] { queryableData.ElementType, queryableData.ElementType }, 
    whereCallExpression, 
    Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe })); 
// ***** End OrderBy ***** 
+1

verdammt, 34 Sekunden hinter! : P –

10

ich Ihre Funktionen erweitert haben, um Unterstützung für Kinder Eigenschaften.

private static LambdaExpression GenerateSelector<TEntity>(String propertyName, out Type resultType) where TEntity : class 
{ 
    // Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField). 
    var parameter = Expression.Parameter(typeof(TEntity), "Entity"); 
    // create the selector part, but support child properties 
    PropertyInfo property; 
    Expression propertyAccess; 
    if (propertyName.Contains('.')) 
    { 
      // support to be sorted on child fields. 
      String[] childProperties = propertyName.Split('.'); 
      property = typeof(TEntity).GetProperty(childProperties[0]); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 
      for (int i = 1; i < childProperties.Length; i++) 
      { 
        property = property.PropertyType.GetProperty(childProperties[i]); 
        propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); 
      } 
    } 
    else 
    { 
      property = typeof(TEntity).GetProperty(propertyName); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    } 
    resultType = property.PropertyType;      
    // Create the order by expression. 
    return Expression.Lambda(propertyAccess, parameter); 
} 

private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, String fieldName) where TEntity : class 
{ 
    Type type = typeof(TEntity); 
    Type selectorResultType; 
    LambdaExpression selector = GenerateSelector<TEntity>(fieldName, out selectorResultType); 
    MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName, 
            new Type[] { type, selectorResultType }, 
            source.Expression, Expression.Quote(selector)); 
    return resultExp; 
} 

Sie können diese Funktionen wie verwenden:

GenerateMethodCall<TEntity>(source, "OrderByDescending", fieldName); 
+1

Du bist mein Held !! –

+0

Ich liebe intelligente Leute –

27

können Sie auch dynamische Linq verwenden

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

C# hier herunterladen http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx

Dann fügen Sie einfach die Verwendung Linq.Dynamic; und Sie erhalten automatisch 2 zusätzliche Erweiterungsmethoden, die so verwendet werden können

return query.OrderBy("StringColumnName"); 
+0

Danke, ich hatte Linq.Dynamic in einer Probe auf Phil Haacks Website gesehen, war sich aber nicht sicher. Ich werde am Wochenende damit spielen. – JTew

+0

Als Alternative kann die Systems.Linq.Dynamic.dll von hier heruntergeladen werden: https://github.com/kahanu/System.Linq.Dynamic – Baig

8

Ich habe Ihre Idee für die Erweiterungsmethode für OrderBy verwendet. Aber im Falle von "viele zu viele" bekomme ich Fehler. Zum Beispiel haben Sie Tabelle Site, Customer und Customer_site. Für die angegebene Site möchte ich nach Kundenname und OrderBy-Erweiterung sortieren (wenn ich "site.customer" passiere, wo der Kunde die Navigationseigenschaft hat), erhalte ich einen Fehler in der Zeile: propertyAccess = Expression.MakeMemberAccess (propertyAccess, property);

Dies ist, was ich (mit einigen Verbesserungen :-)) verwenden:

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class 
{ 
    IQueryable<TEntity> returnValue = null; 

    string orderPair = orderByValues.Trim().Split(',')[0]; 
    string command = orderPair.ToUpper().Contains("DESC") ? "OrderByDescending" : "OrderBy"; 

    var type = typeof(TEntity); 
    var parameter = Expression.Parameter(type, "p"); 

    string propertyName = (orderPair.Split(' ')[0]).Trim(); 

    System.Reflection.PropertyInfo property; 
    MemberExpression propertyAccess; 

    if (propertyName.Contains('.')) 
    { 
    // support to be sorted on child fields. 
    String[] childProperties = propertyName.Split('.'); 
    property = typeof(TEntity).GetProperty(childProperties[0]); 
    propertyAccess = Expression.MakeMemberAccess(parameter, property); 

    for (int i = 1; i < childProperties.Length; i++) 
    { 
     Type t = property.PropertyType; 
     if (!t.IsGenericType) 
     { 
     property = t.GetProperty(childProperties[i]); 
     } 
     else 
     { 
     property = t.GetGenericArguments().First().GetProperty(childProperties[i]); 
     } 

     propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); 
    } 
    } 
    else 
    { 
    property = type.GetProperty(propertyName); 
    propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    } 

    var orderByExpression = Expression.Lambda(propertyAccess, parameter); 

    var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType }, 

    source.Expression, Expression.Quote(orderByExpression)); 

    returnValue = source.Provider.CreateQuery<TEntity>(resultExpression); 

    if (orderByValues.Trim().Split(',').Count() > 1) 
    { 
    // remove first item 
    string newSearchForWords = orderByValues.ToString().Remove(0, orderByValues.ToString().IndexOf(',') + 1); 
    return source.OrderBy(newSearchForWords); 
    } 

    return returnValue; 
} 

Grüße

Slobodan

1

Wenn Sie in der Lage sind „System.Linq hinzuzufügen.Dynamic“Paket dann, Zu einfach ohne Komplikationen,

fisrt insatll Paket "System.Linq.Dynamic" von NuGet Paket-Manager dann versuchen, wie unten als Ihre Notwendigkeit,

Ex:

public IQueryable<TEntity> GetWithInclude(Expression<Func<TEntity, bool>> predicate, 
        List<string> sortBy, int pageNo, int pageSize = 12, params string[] include) 
     { 
      try 
      { 
       var numberOfRecordsToSkip = pageNo * pageSize; 
       var dynamic = DbSet.AsQueryable(); 

       foreach (var s in include) 
       { 
        dynamic.Include(s); 
       } 
       return dynamic.OrderBy("CreatedDate").Skip(numberOfRecordsToSkip).Take(pageSize); 


      } 
      catch (Exception e) 
      { 
       throw new Exception(e.Message); 
      } 
     } 

Hope this

0

helfen fixiert ich diesen Code ein bisschen: https://stackoverflow.com/a/1670085/5852630

Dieser Code funktioniert mit sequentieller Sortierung: (! Nicht "OrderBy") zuerst "OrderBy" ausführen, dann "ThenBy"

public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class 
{ 
    IQueryable<TEntity> returnValue = null; 

    string[] orderPairs = orderByValues.Trim().Split(','); 

    Expression resultExpression = source.Expression; 

    string strAsc = "OrderBy"; 
    string strDesc = "OrderByDescending"; 

    foreach (string orderPair in orderPairs) 
    { 
     if (string.IsNullOrWhiteSpace(orderPair)) 
      continue; 

     string[] orderPairArr = orderPair.Trim().Split(' '); 

     string propertyName = orderPairArr[0].Trim(); 
     string orderNarrow = orderPairArr.Length > 1 ? orderPairArr[1].Trim() : string.Empty; 

     string command = orderNarrow.ToUpper().Contains("DESC") ? strDesc : strAsc; 

     Type type = typeof(TEntity); 
     ParameterExpression parameter = Expression.Parameter(type, "p"); 

     System.Reflection.PropertyInfo property; 
     Expression propertyAccess; 

     if (propertyName.Contains('.')) 
     { 
      // support to be sorted on child fields. 
      String[] childProperties = propertyName.Split('.'); 
      property = typeof(TEntity).GetProperty(childProperties[0]); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 

      for (int i = 1; i < childProperties.Length; i++) 
      { 
       Type t = property.PropertyType; 
       if (!t.IsGenericType) 
       { 
        property = t.GetProperty(childProperties[i]); 
       } 
       else 
       { 
        property = t.GetGenericArguments().First().GetProperty(childProperties[i]); 
       } 

       propertyAccess = Expression.MakeMemberAccess(propertyAccess, property); 
      } 
     } 
     else 
     { 
      property = type.GetProperty(propertyName); 
      propertyAccess = Expression.MakeMemberAccess(parameter, property); 
     } 

     if (property.PropertyType == typeof(object)) 
     { 
      propertyAccess = Expression.Call(propertyAccess, "ToString", null); 
     } 

     LambdaExpression orderByExpression = Expression.Lambda(propertyAccess, parameter); 

     resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType == typeof(object) ? typeof(string) : property.PropertyType }, 
      resultExpression, Expression.Quote(orderByExpression)); 

     strAsc = "ThenBy"; 
     strDesc = "ThenByDescending"; 
    } 

    returnValue = source.Provider.CreateQuery<TEntity>(resultExpression); 

    return returnValue; 
}