IEnumerable OrderBy by String 复杂 属性

IEnumerable OrderBy by String with complex property

我需要按字符串对查询进行排序,发现 this 效果很好。问题是它无法处理 复杂属性 相关实体,例如 "ComplexProperty.Property""RelatedEntity.Property" 因为它只搜索主要 class.

我想我应该能够通过 .并以某种方式递归地检查每种类型,但我无法确切地弄清楚如何。这是可能的还是我走投无路了?

这样做的原因是我有一个 "old" 解决方案 (MVC 3),它打开了 webgrid,并且 webgrid 需要所有数据来进行排序(它是一个分离的 EF4 解决方案),它只需要很多时间。我需要将排序传递到查询中,并且只在分页中检索该页面的帖子。 webgrid 使用 sort 和 sortdir 参数调用相同的控制器,并使用 ajax.

进行更新

如果不可能,也许我应该看看另一个解决方案,任何人都可以提示一下?

编辑说明 今天,该解决方案获取所有违规,将它们发送到 webGrid 并让 webgrid 进行分页和排序。该解决方案已经存在多年,违规 table 已经发展到页面非常慢的程度,主要是因为每次都获取所有数据。我已经实现了对存储库的分页以仅接收 class 的一部分。今天的存储库使用 IEnumerable 和表示层和存储库之间的 ServiceLayer(业务),总是 returns 到表示层的列表。

这是我希望能够使用的SQL

SELECT vi.ViolationId, ar.AreaName
  FROM [Violation] vi
  join [ControlPoint] cp on vi.ControlPointId = cp.ControlPointId
  join [Area] ar on ar.AreaId = cp.AreaId
order by ar.AreaName

我需要像这样使用 orderBy(string sortExpression) 来完成此操作。

violations.OrderBy("ControlPoint.Area.AreaName")

并找到这个函数(上面链接)作为它的基础。

    public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> list, string sortExpression)
    {
        sortExpression += "";
        string[] parts = sortExpression.Split(' ');
        bool descending = false;
        string property = "";

        if (parts.Length > 0 && parts[0] != "")
        {
            property = parts[0];

            if (parts.Length > 1)
            {
                descending = parts[1].ToLower().Contains("esc");
            }

            PropertyInfo prop = typeof(T).GetProperty(property);

            // handle Prop.SubProp
            // prop.GetType().GetProperty

            if (prop == null)
            {
                throw new Exception("No property '" + property + "' in + " + typeof(T).Name + "'");
            }

            if (descending)
                return list.OrderByDescending(x => prop.GetValue(x, null));
            else
                return list.OrderBy(x => prop.GetValue(x, null));
        }
        return list;
    }

好的,现在它开始工作了,我 post 我的最终结果。感谢所有的意见,如果没有所有评论,我会认为这是不可行的

首先是所有的辅助方法

public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string property, bool descending)
    {
        if(!descending)
            return ApplyOrder<T>(source, property, "OrderBy");
        else
            return ApplyOrder<T>(source, property, "OrderByDescending");
    }

    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
    {
        return ApplyOrder<T>(source, property, "OrderBy");
    }
    public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
    {
        return ApplyOrder<T>(source, property, "OrderByDescending");
    }
    public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
    {
        return ApplyOrder<T>(source, property, "ThenBy");
    }
    public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
    {
        return ApplyOrder<T>(source, property, "ThenByDescending");
    }
    static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
    {
        string[] props = property.Split('.');
        Type type = typeof(T);
        ParameterExpression arg = Expression.Parameter(type, "x");
        Expression expr = arg;
        foreach (string prop in props)
        {
            // use reflection (not ComponentModel) to mirror LINQ
            PropertyInfo pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }
        Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
        LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);

        object 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;
    }

    public static bool TryParseSortText(this string sortExpression, out string property, out bool descending)
    {
        descending = false;
        property = string.Empty;

        if (string.IsNullOrEmpty(sortExpression))
            return false;

        string[] parts = sortExpression.Split(' ');

        if (parts.Length > 0 && !string.IsNullOrEmpty(parts[0]))
        {
            property = parts[0];

            if (parts.Length > 1)
            {
                descending = parts[1].ToLower().Contains("esc");
            }
        }
        else
            return false;

        return true;
    }

然后在服务层我有这个

    public PagedResult<Violation> GetViolationByModule(PagedRequest pagedRequest, long moduleId, long stakeHolderId, Expression<Func<Violation, bool>> filter, string sort = "")
    {
        return ExceptionManager.Process(() => _GetViolationByModule(pagedRequest, moduleId, stakeHolderId, filter, sort),
         "ServicePolicy");

    }

    private PagedResult<Violation> _GetViolationByModule(PagedRequest pagedRequest, long moduleId, long stakeHolderId, Expression<Func<Violation, bool>> filter, string sort = "")
    {
        var query = ViolationRepository.GetViolationByModule(moduleId, stakeHolderId);

        if(!string.IsNullOrEmpty(sort))
        {
            string sortProperty = string.Empty;
            bool desc = false;
            if(sort.TryParseSortText(out sortProperty, out desc))
            {
                query = query.OrderBy(sortProperty, desc);
            }

        }
        if (filter != null)
        {
            query = query.Where(filter);
        }

        var violations = _GetPagedResult(pagedRequest, query);
        foreach (var violation in violations.Results)
        {
            var user = FrontendUserRepository.GetFrontendUserByName(violation.ReportBy);
            if (user != null)
            {
                violation.ReportUserInitial = user.Initial;
            }
            else
            {
                violation.ReportUserInitial = violation.ReportBy;
            }

        }

        return violations;
    }

我可以通过

从控制器调用它
        var pagedUnamendedViolationList = ViolationService.GetViolationByModule(new PagedRequest() { Page = page, PageSize = pageSize },
            moduleId,
            stakeholderId,
            v => (fUser == null || v.ControlPoint.Area.FronendUsers.Contains(fUser)) && !v.IsAmended,
            "ControlPoint.Area.AreaName DESC" 
            );