【问题标题】:Performing dynamic sorts on EF4 data对 EF4 数据执行动态排序
【发布时间】:2010-05-03 17:04:16
【问题描述】:

我正在尝试对放入网格中的数据执行动态排序,并将其放入我们的 MVC UI。由于 MVC 是通过 WCF 从其他所有内容中抽象出来的,因此我创建了几个实用程序类和扩展来帮助解决这个问题。最重要的两件事(稍微简化)如下:

    public static IQueryable<TModel> ApplySortOptions<TModel, TProperty>(this IQueryable<TModel> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
    {
        var sortedSortOptions = (from o in sortOptions
                                 orderby o.Priority ascending
                                 select o).ToList();

        var results = collection;

        foreach (var option in sortedSortOptions)
        {
            var currentOption = option;
            var propertyName = currentOption.Property.MemberWithoutInstance();
            var isAscending = currentOption.IsAscending;

            if (isAscending)
            {
                results = from r in results
                          orderby propertyName ascending 
                          select r;
            }
            else
            {
                results = from r in results
                          orderby propertyName descending 
                          select r;
            }
        }

        return results;
    }


public interface ISortOption<TModel, TProperty> where TModel : class
{
    Expression<Func<TModel, TProperty>> Property { get; set; }
    bool IsAscending { get; set; }
    int Priority { get; set; }
}

我没有为您提供MemberWithoutInstance() 的实现,但请相信我,它会将属性名称作为字符串返回。 :-)

以下是我将如何使用它的示例(使用ISortOption&lt;TModel, TProperty&gt; 的一个无趣的基本实现):

var query = from b in CurrentContext.Businesses
            select b;

var sortOptions = new List<ISortOption<Business, object>>
                      {
                          new SortOption<Business, object>
                              {
                                  Property = (x => x.Name),
                                  IsAscending = true,
                                  Priority = 0
                              }
                      };

var results = query.ApplySortOptions(sortOptions);

正如我在this question 中发现的那样,问题是特定于我的orderby propertyName ascendingorderby propertyName descending 行(据我所知,其他一切都很好)。如何以正常工作的动态/通用方式执行此操作?

【问题讨论】:

    标签: sql-server-2008 visual-studio-2010 linq-to-entities entity-framework-4


    【解决方案1】:

    您真的应该考虑为此使用Dynamic LINQ。事实上,您可以选择简单地按名称列出属性,而不是使用表达式,这样更容易构造。

    public static IQueryable<T> ApplySortOptions<T, TModel, TProperty>(this IQueryable<T> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class  
    {    
        var results = collection;  
    
        foreach (var option in sortOptions.OrderBy( o => o.Priority ))  
        {  
            var currentOption = option;  
            var propertyName = currentOption.Property.MemberWithoutInstance();  
            var isAscending = currentOption.IsAscending;  
    
             results = results.OrderBy( string.Format( "{0}{1}", propertyName, !isAscending ? " desc" : null ) );
        }  
    
        return results;  
    }
    

    【讨论】:

    • 我忘记了动态 LINQ 库(嗯,这真是个意外的双关语!)
    • 虽然我没有采用这个解决方案,但我最终还是使用了 Dynamic LINQ 来帮助我解决一些我遇到的序列化问题。
    【解决方案2】:

    虽然我认为@tvanfosson 的解决方案会完美运行,但我也在研究这种可能性:

        /// <summary>
        /// This extension method is used to help us apply ISortOptions to an IQueryable.
        /// </summary>
        /// <param name="collection">This is the IQueryable you wish to apply the ISortOptions to.</param>
        /// <param name="sortOptions">These are the ISortOptions you wish to have applied. You must specify at least one ISortOption (otherwise, don't call this method).</param>
        /// <returns>This returns an IQueryable object.</returns>
        /// <remarks>This extension method should honor deferred execution on the IQueryable that is passed in.</remarks>
        public static IOrderedQueryable<TModel> ApplySortOptions<TModel, TProperty>(this IQueryable<TModel> collection, IEnumerable<ISortOption<TModel, TProperty>> sortOptions) where TModel : class
        {
            Debug.Assert(sortOptions != null, "ApplySortOptions cannot accept a null sortOptions input.");
            Debug.Assert(sortOptions.Count() > 0, "At least one sort order must be specified to ApplySortOptions' sortOptions input.");
    
            var firstSortOption = sortOptions.OrderBy(o => o.Priority).First();
            var propertyName = firstSortOption.Property.MemberWithoutInstance();
            var isAscending = firstSortOption.IsAscending;
    
            // Perform the first sort action
            var results = isAscending ? collection.OrderBy(propertyName) : collection.OrderByDescending(propertyName);
    
            // Loop through all of the rest ISortOptions
            foreach (var sortOption in sortOptions.OrderBy(o => o.Priority).Skip(1))
            {
                // Make a copy of this or our deferred execution will bite us later.
                var currentOption = sortOption;
    
                propertyName = currentOption.Property.MemberWithoutInstance();
                isAscending = currentOption.IsAscending;
    
                // Perform the additional orderings.
                results = isAscending ? results.ThenBy(propertyName) : results.ThenByDescending(propertyName);
            }
    
            return results;
        }
    

    使用来自 questionanswer 的代码:

        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
        {
            return ApplyOrder(source, property, "OrderBy");
        }
    
        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
        {
            return ApplyOrder(source, property, "OrderByDescending");
        }
    
        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
        {
            return ApplyOrder(source, property, "ThenBy");
        }
    
        public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
        {
            return ApplyOrder(source, property, "ThenByDescending");
        }
    
        private static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
        {
            var props = property.Split('.');
            var type = typeof (T);
            var arg = Expression.Parameter(type, "x");
            Expression expr = arg;
            foreach (var prop in props)
            {
                // use reflection (not ComponentModel) to mirror LINQ
                var pi = type.GetProperty(prop);
                expr = Expression.Property(expr, pi);
                type = pi.PropertyType;
            }
            var delegateType = typeof (Func<,>).MakeGenericType(typeof (T), type);
            var lambda = Expression.Lambda(delegateType, expr, arg);
    
            var result = typeof (Queryable).GetMethods().Single(
                method => method.Name == methodName
                          && method.IsGenericMethodDefinition
                          && method.GetGenericArguments().Length == 2
                          && method.GetParameters().Length == 2)
                .MakeGenericMethod(typeof (T), type)
                .Invoke(null, new object[] {source, lambda});
            return (IOrderedQueryable<T>) result;
        }
    

    【讨论】:

    • 我实际上是使用这个基于反射的代码来做到这一点的。虽然很有趣 - 我最终使用动态 LINQ 进行“表达式序列化”,所以我可以通过 WCF 获取这些 SortOrder。
    猜你喜欢
    • 2010-10-26
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-25
    相关资源
    最近更新 更多