2013-04-18 35 views
3

我在ASP.NET MVC 3站点的相当多的页面中使用了数据表。他们使用服务器端分页,现在我想实现基于列标题的排序。 Datatables自带iSortCol_0,它是点击哪个列的int值。在LINQ中获取动态OrderBy

我不喜欢这种方法,因为查询最终会是这样的:

if(iSortCol_0 == 0) 
{  
    // query 
    // OderBy(x => x.CarName) 
} 

那么这将是重复每列(加上else子句每个由降订购)。所以我改变了我的方法,现在将列名传递给服务器。

我想出了以下内容:

Expression<Func<vw_Car, string>> sortExpression1 = null; 
Expression<Func<vw_Car, int>> sortExpression2 = null; 

switch(columnToSort) 
{ 
    case "InvoiceNo": sortExpression1 = x => x.CarNo; break; 
    case "ClientNumber": sortExpression1 = x => x.ForeName; break; 
    case "ClientName": sortExpression1 = x => x.SurName; break; 
    default: sortExpression2 = x => x.Age.Value; break; 
} 

// start of query 
.OrderByDir(sortDirection, sortExpression1 , sortExpression2) 

现在OrderByDir看起来象下面这样:

public static IOrderedQueryable<T> OrderByDir<T>(this IQueryable<T> source, string dir, Expression<Func<T, string>> column1, Expression<Func<T, int>> column2) 
{ 
    if (column1 != null) 
    { 
     return dir == "asc" ? source.OrderBy(column1) : source.OrderByDescending(column1); 
    }   
    if (column2 != null) 
    { 
     return dir == "asc" ? source.OrderBy(column2) : source.OrderByDescending(column2); 
    }  
    return null; 
} 

这工作,因为它排序是string型或int列。我有另一列DateTime,我想排序,所以我需要写另一个sortExpression3,然后将其添加到我的OrderByDir

但是我真的不喜欢的实现 - 我曾试图写了一个对象作为第二个参数,而不是stringintDatetime,但是当有到位的代码,我得到一个通用的排序表达式无法将类型“System.DateTime”强制转换为键入“System.Object”。 LINQ to Entities只支持铸造实体数据模型基元类型

任何一个任何想法,可能更好的方法这样做呢?

+0

为什么有两个sortExpressions?根据你的代码,无论是表达式1还是表达式2都被设置,但从不同时。它是否正确? –

+0

@mattytommo:不适用,引用“我假设你只需要支持LINQ to Objects”。 –

+0

@DanielHilgarth - 因为其中一个排序表达式是Age列(一个int字段) - 另一个排序表达式是针对字符串列 –

回答

7

这样的扩展名应该符合你的需求:

public static class LinqExtensions 
{ 
    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property) 
    { 
     return ApplyOrder(source, property, "OrderBy"); 
    } 

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

    private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName) 
    { 
     var props = property.Split('.'); 
     var type = typeof(T); 
     var arg = Expression.Parameter(type, "x"); 
     Expression expr = arg; 
     foreach (var prop in props) 
     { 
      // use reflection (not ComponentModel) to mirror LINQ 
      var pi = type.GetProperty(prop); 
      expr = Expression.Property(expr, pi); 
      type = pi.PropertyType; 
     } 
     var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type); 
     var lambda = Expression.Lambda(delegateType, expr, arg); 

     var 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 (IOrderedQueryable<T>)result; 
    } 
} 

调用例如:

query.OrderBy("Age.Value"); 
+0

我建议将订单方向添加为参数,而不是单独的方法。 – maxlego

+0

它可以完成,但这里的线索是lambda表达式树的构建和执行。其他次要变化是主观偏好的问题。 – jwaliszko