2016-11-29 3 views
0

ich eine Erweiterungsmethode für die Bestellung haben, die SortExpression kann so etwas wie „Beschreibung“ oder „Beschreibung DESC“ sein, es funktioniert perfekt für Spalten am selben Tisch:Entity Framework Ordnung mit Reflexion

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortExpression) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 

     if (string.IsNullOrEmpty(sortExpression)) 
      return source; 

     var parts = sortExpression.Split(' '); 
     var isDescending = false; 
     var propertyName = ""; 
     var type = typeof(T); 

     if (parts.Length > 0 && parts[0] != "") 
     { 
      propertyName = parts[0]; 

      if (parts.Length > 1) 
       isDescending = parts[1].ToLower().Contains("esc"); 

      var prop = type.GetProperty(propertyName); 
      if (prop == null) 
       throw new ArgumentException(string.Format("No property '{0}' on type '{1}'", propertyName, type.Name)); 

      var funcType = typeof(Func<,>) 
       .MakeGenericType(type, prop.PropertyType); 

      var lambdaBuilder = typeof(Expression) 
       .GetMethods() 
       .First(x => x.Name == "Lambda" && x.ContainsGenericParameters && x.GetParameters().Length == 2) 
       .MakeGenericMethod(funcType); 

      var parameter = Expression.Parameter(type); 
      var propExpress = Expression.Property(parameter, prop); 

      var sortLambda = lambdaBuilder 
       .Invoke(null, new object[] { propExpress, new ParameterExpression[] { parameter } }); 

      var sorter = typeof(Queryable) 
       .GetMethods() 
       .FirstOrDefault(x => x.Name == (isDescending ? "OrderByDescending" : "OrderBy") && x.GetParameters().Length == 2) 
       .MakeGenericMethod(new[] { type, prop.PropertyType }); 

      var result = (IQueryable<T>)sorter 
       .Invoke(null, new object[] { source, sortLambda }); 
      return result; 
     } 

     return source; 
    } 

Arbeits Beispiel:

var query = db.Audit.Include("AccessLevel").AsQueryable(); 
    query = query.OrderBy("Description"); 

Bitte beachten Sie, dass die Spalte "Beschreibung" in der gleichen Tabelle "Audit" existiert.

Was ich versuche zu tun, um eine Spalte in einer Beziehungstabelle zu sortieren:

Wie die folgenden

var query = db.Audit.Include("AccessLevel").AsQueryable(); 
    query = query.OrderBy("AccessLevel.Name"); 

Welche äquivalent zu:

query = query.OrderBy(o => o.AccessLevel.Name); 

Was ist die erforderliche Änderung an meiner Erweiterungsmethode?

Antwort

0

Ich löste es durch den folgenden Code:

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string property) 
     { 

       return ApplyOrder<T>(source, parts[0], "OrderBy"); 

     } 
     public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property) 
     { 
      return ApplyOrder<T>(source, property, "OrderByDescending"); 
     } 
     public static IQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property) 
     { 
      return ApplyOrder<T>(source, property, "ThenBy"); 
     } 
     public static IQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property) 
     { 
      return ApplyOrder<T>(source, property, "ThenByDescending"); 
     } 
     static IQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName) 
     { 

      if (source == null) 
       throw new ArgumentNullException("source"); 

      if (string.IsNullOrEmpty(property)) 
       return source; 


      string[] props = property.Split('.'); 
      Type type = typeof(T); 
      ParameterExpression arg = Expression.Parameter(type, "x"); 
      Expression expr = arg; 
      foreach (string prop in props) 
      { 
       // use reflection (not ComponentModel) to mirror LINQ 
       PropertyInfo pi = type.GetProperty(prop); 
       expr = Expression.Property(expr, pi); 
       type = pi.PropertyType; 
      } 
      Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type); 
      LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg); 

      object result = typeof(Queryable).GetMethods().Single(
        method => method.Name == methodName 
          && method.IsGenericMethodDefinition 
          && method.GetGenericArguments().Length == 2 
          && method.GetParameters().Length == 2) 
        .MakeGenericMethod(typeof(T), type) 
        .Invoke(null, new object[] { source, lambda }); 
      return (IQueryable<T>)result; 
     }