使用反射调用OrderBy(System.Linq.Enumerable) [英] Invoking OrderBy (System.Linq.Enumerable) with Reflection

查看:209
本文介绍了使用反射调用OrderBy(System.Linq.Enumerable)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用开源系统,并且已被要求根据术语的相关性对一组实体进行排序.

I am working with an open-source system and have been given the requirement to sort a set of entities by relevance to a term.

首先需要对集合进行精确匹配,然后对"startswith"进行匹配,然后对"contains"进行匹配.

The set needs be ordered with exact matches first, then the "startswith" matches, then the "contains" matches.

我为实现这一目的而想到的lambda表达式是:

The lambda expression I came up with to achieve this is:

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

目前,预编写系统的工作方式是,它使用带有反射的lambda表达式的实例调用OrderBy方法,以便对结果进行排序.

At present, the pre-written system works such that it invokes an OrderBy method with an instance of a lambda expression with reflection, in order to sort the results.

为了尽量减少在此任务上花费的时间,我希望遵守此约定.我试图创建一个lambda表达式的实例,并将其作为参数传递给MethodInfo.Invoke(),但由于其中包含变量,因此会引发异常.

To minimise the amount of time spent on this task, I would like to conform to this convention. I've tried to create an instance of a lambda expression and pass it in as a parameter to MethodInfo.Invoke() but because it's got a variable in it, it throws an exception.

此系统的sort方法的代码为:

The code for the sort method of this system is:

        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进行一些晦涩的方法解析(这是一个邪恶的hack,但在这里非常值得-手动执行此操作不会增加任何用处).我不是依靠具有searchTerm的测试类"的未定义范围,而是将其提升到了最高级别-但是您可能需要一些不同的内容(因此,我在关于):

This is about as short as I can make it, using dynamic to do some of the obscure method resolution (an evil hack, but very worthwhile here - doing it manually adds nothing useful). Rather than rely on the undefined scope of the "test class" that has the searchTerm, I've hoisted that up to the top level - but you may need something different (hence my continuing questions in comments about the scope of 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作为外观):

With example usage (here using LINQ-to-Objects as a facade):

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

这篇关于使用反射调用OrderBy(System.Linq.Enumerable)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆