2013-07-22 136 views
1

经过一定剂量的谷歌搜索和尝试一些事情,没有找到/得到期望的结果后,我决定发布这个问题。将IComparer参数传递给自定义LINQ OrderBy扩展方法

我有一个定制OrderBy扩展方法和现在正执行OrderBy操作时,我想传递一个AlphanumComparator这样的:

return divergences.OrderBy(sort, new AlphanumComparator()); 

这里的扩展方法:

public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection, 
    GridSortOptions sortOptions, AlphanumComparator comparer = null) 
{ 
    if (string.IsNullOrEmpty(sortOptions.Column)) 
    { 
     return collection; 
    } 

    Type collectionType = typeof(T); 

    ParameterExpression parameterExpression = Expression.Parameter(collectionType, "p"); 

    Expression seedExpression = parameterExpression; 

    Expression aggregateExpression = sortOptions.Column.Split('.').Aggregate(seedExpression, Expression.Property); 

    MemberExpression memberExpression = aggregateExpression as MemberExpression; 

    if (memberExpression == null) 
    { 
     throw new NullReferenceException(string.Format("Unable to cast Member Expression for given path: {0}.", sortOptions.Column)); 
    } 

    LambdaExpression orderByExp = Expression.Lambda(memberExpression, parameterExpression); 

    const string orderBy = "OrderBy"; 

    const string orderByDesc = "OrderByDescending"; 

    Type childPropertyType = ((PropertyInfo)(memberExpression.Member)).PropertyType; 

    string methodToInvoke = sortOptions.Direction == MvcContrib.Sorting.SortDirection.Ascending ? orderBy : orderByDesc; 

    MethodCallExpression orderByCall; 

    orderByCall = Expression.Call(typeof(Queryable), methodToInvoke, new[] { collectionType, childPropertyType }, collection.Expression, Expression.Quote(orderByExp)); 

    if(comparer != null) 
    { 
     // How can I pass the comparator to the OrderBy MethodCallExpression? 

     // Using the standard LINQ OrderBy, we can do this: 
     // elements.OrderBy(e => e.Index, new AlphanumComparator()) 
    } 

    return collection.Provider.CreateQuery<T>(orderByCall); 
} 

见代码中的评论我认为我应该通过IComparer ......我该如何处理这个问题?

+0

从根本上说,你在这里有一个问题 - 你期待一个*任意比较器*被转换成SQL。你期望如何工作?如果你在自己的代码中实现了'IComparer ',并通过哈希代码进行排序,那么你期望生成的SQL看起来像什么呢? –

+0

@JonSkeet如果我将参数声明为“AlphanumComparator”而不是任意的'IComparer ',该怎么办?我只是通过比较器的特定属性,我知道是字符串类型。 –

+0

假设这是您自己的类型,它也有同样的问题:LINQ提供程序中的任何内容都不知道如何处理它。 –

回答

0

我不得不采取不同的方式。

我试图创建一个通用OrderByMvcContrib Grid使用,但经过IComparer到定制OrderBy表达我想象它会工作没有工作。

因此,我创建这个辅助接收的字符串中的点表示法等Element1.Standard.Chapter.Manual.Name,然后返回一个Expression<Func<T, string>>

public static Func<T, string> CreateSelectorExpression<T>(string propertyName) where T : class 
{ 
    ParameterExpression parameterExpression = Expression.Parameter(typeof(T)); 

    Expression aggregateExpression = propertyName.Split('.'). 
     Aggregate(parameterExpression as Expression, Expression.Property) as MemberExpression; 

    LambdaExpression exp = Expression.Lambda(aggregateExpression, parameterExpression); 

    return (Func<T, string>)exp.Compile(); 
} 

然后将该表达键入到T(在这种情况下Divergence对象类型)可被传递(见func.Invoke),以标准的LINQ OrderBy算哪里我也可以通过自定义IComparerAlphanumComparator这样的:

if (sort.Column.Contains("Index")) 
{ 
    var func = Helpers.ExtensionMethods.CreateSelectorExpression<Divergence>(sort.Column); 

    if (sort.Direction == SortDirection.Ascending) 
    { 
     return divergences.OrderBy(func, new AlphanumComparator()); 
    } 
    else 
    { 
     return divergences.OrderByDescending(func, new AlphanumComparator()); 
    } 
} 

这涉及到更多的工作,但以通用方式解决了我想要的问题。

+0

你意识到现在这个排序*不会在数据库中发生,查询中以后的任何其他内容也会在本地执行,对吧? –

+0

当然@JonSkeet ...这是我发现按照我想要的方式工作的唯一方法,因为自定义的'IComparer'只能在本地执行,而不能像您提到的那样在数据库端执行。你看到有什么方法可以改进这个代码吗? –

+0

嗯,目前还不清楚为什么你现在需要创建一个'Expression ' - 你只需要一个委托。我建议你改变'CreateSelectorExpression'返回'Func ',只需通过反射获取属性并调用'Delegate.CreateDelegate'。 –

相关问题