问题描述
在谷歌搜索并尝试了一些东西但没有找到/得到想要的结果后,我决定发布这个问题.
我有一个定制的 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 的代码中的注释...我该如何处理?
推荐答案
我必须以不同的方式处理这个问题.
我试图创建一个通用的 OrderBy 以与 MvcContrib 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 运算符,我也可以传递自定义的 IComparer AlphanumComparator 像这个:
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()); } }
这涉及更多工作,但以我希望的方式以通用方式解决了问题.
问题描述
After a good dose of Googling and trying some things and not finding/getting the desired result I decided to post this question.
I have a custom made OrderBy extension method and now when performing the OrderBy operation I'd like to pass an AlphanumComparator like this:
return divergences.OrderBy(sort, new AlphanumComparator());
Here's the extension method:
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); }
See the comment in the code where I think I should pass the IComparer... how could I approach this?
推荐答案
I had to approach this differently.
I was trying to create a generic OrderBy to be used with MvcContrib Grid, but passing the IComparer to that custom OrderBy expression did not work as I imagined it would work.
So I created this helper that receives a string in dot notation like Element1.Standard.Chapter.Manual.Name and then returns an 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(); }
This expression typed to T (in this case Divergence object type) can then be passed (see func.Invoke) to the standard LINQ OrderBy operator where I can also pass the custom IComparer AlphanumComparator like this:
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()); } }
This involved a little bit more work but solved the problem in a generic fashion the way I wanted it to be.