簡體   English   中英

使用反射調用OrderBy(System.Linq.Enumerable)

[英]Invoking OrderBy (System.Linq.Enumerable) with Reflection

我正在使用開源系統,並且已被要求根據術語的相關性對一組實體進行排序。

首先需要對集合進行精確匹配,然后再對“ startswith”進行匹配,然后對“ contains”進行匹配。

我想出的lambda表達式是:

var results = products.OrderBy(p => p.Name.StartsWith(searchTerm) ? Convert.ToString((char)0) : p.Name);

當前,預編寫系統的工作方式是,它使用帶有反射的lambda表達式的實例調用OrderBy方法,以便對結果進行排序。

為了盡量減少在此任務上花費的時間,我希望遵守此約定。 我試圖創建一個lambda表達式的實例,並將其作為參數傳遞給MethodInfo.Invoke(),但由於其中包含變量,因此會引發異常。

該系統的sort方法的代碼為:

        private IOrderedQueryable<ReferenceProduct> OrderProducts(IQueryable<ReferenceProduct> filteredProducts, String sortExpression, String sortDirection, out String checkedSortExpression, out String checkedSortedDescending)
    {
        ProductListManager.SortExpression correspondingSortExpression = this.SortExpressions.OrderByDescending(se => String.Equals(se.Code, sortExpression, StringComparison.OrdinalIgnoreCase))
                                                                                            .DefaultIfEmpty(new SortExpression<Guid> { Code = String.Empty, Expression = rp => rp.ProductId })
                                                                                            .FirstOrDefault();
        checkedSortExpression = correspondingSortExpression.Code;
        checkedSortedDescending = String.Equals("desc", sortDirection, StringComparison.OrdinalIgnoreCase) ? "desc" : "asc";
        MethodInfo orderMethod = (checkedSortedDescending.Equals("desc", StringComparison.OrdinalIgnoreCase) ? (Queryable.OrderByDescending) : new Func<IQueryable<Object>, Expression<Func<Object, Object>>, IOrderedQueryable<Object>>(Queryable.OrderBy)).Method.GetGenericMethodDefinition().MakeGenericMethod(typeof(ReferenceProduct), correspondingSortExpression.Expression.ReturnType);
        IOrderedQueryable<ReferenceProduct> orderedProducts = orderMethod.Invoke(null, new Object[] { filteredProducts, correspondingSortExpression.Expression }) as IOrderedQueryable<ReferenceProduct>;
        return orderedProducts;
    }

有人知道嗎?

提前致謝。

瑞安

這差不多是我能做到的,它使用dynamic來完成一些晦澀的方法解析(這是一個邪惡的技巧,但在這里非常值得-手動進行操作並沒有增加任何用處)。 我不是依靠具有searchTerm的“測試類”的未定義范圍,而是將其提升到了最高級別-但是您可能需要一些不同的東西(因此,我在有關searchTerm范圍的注釋中繼續提出疑問):

class ReferenceProduct {
    public string Name { get; set; }

    static readonly Dictionary<string, Func<string, LambdaExpression>> knownSortFactories =
    new Dictionary<string, Func<string,LambdaExpression>> {
        {"name", searchTerm =>(Expression<Func<ReferenceProduct, string>>)(p => p.Name.StartsWith(searchTerm) ? Convert.ToString((char)0) : p.Name) }
        // ... more here
    };

    public static IOrderedQueryable<ReferenceProduct> OrderProducts(IQueryable<ReferenceProduct> filteredProducts, string sortExpression, string sortDirection, string queryTerm)
    {
        Func<string, LambdaExpression> factory;
        if (!knownSortFactories.TryGetValue(sortExpression, out factory))
            throw new InvalidOperationException("Unknown sort expression: " + sortExpression);
        dynamic lambda = factory(queryTerm); // evil happens here ;p
        switch(sortDirection)
        {
            case "asc":
                return Queryable.OrderBy(filteredProducts, lambda);
            case "desc":
                return Queryable.OrderByDescending(filteredProducts, lambda);
            default:
                throw new InvalidOperationException("Unknown sort direction: " + sortDirection);
        }
    }
}

使用示例用法(此處使用LINQ-to-Objects作為外觀):

static void Main()
{
    var source = new[] {
        new ReferenceProduct { Name = "def" },
        new ReferenceProduct { Name = "fooghi" },
        new ReferenceProduct { Name = "abc" }
    }.AsQueryable();
    var sorted = ReferenceProduct.OrderProducts(source, "name", "asc", "foo");
    var arr = sorted.ToArray(); 
    foreach(var item in arr) {
        Console.WriteLine(item.Name);
    }
}

輸出:

fooghi
abc
def

完全不需要反射調用就可以實現您想要的目標。

代替

var orderedProducts = orderMethod.Invoke(null, new Object[] { filteredProducts, correspondingSortExpression.Expression }) as IOrderedQueryable<ReferenceProduct>;

只需使用查詢提供程序:

var orderedProducts = filteresProducts.Provider.CreateQuery<ReferenceProduct>(Expression.Call(null, orderMethod, Expression.Constant(correspondingSortExpression.Expression)));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM