2013-07-22 132 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'。 –

相關問題