![](/img/trans.png)
[英]Why there is no SelectToArray method in 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.