c# - 具有复杂属性的 IEnumerable OrderBy by String

标签 c# entity-framework ienumerable webgrid

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

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

这样做的原因是我有一个带有 webgrid 的“旧”解决方案 (MVC 3),并且 webgrid 需要所有数据来进行排序(它是一个分离的 EF4 解决方案)并且它只需要很多时间。我需要将排序传递到查询中,并且只在分页中检索该页面的帖子。 webgrid 使用 sort 和 sortdir 参数调用同一个 Controller ,并使用 ajax 更新。

如果不可能,也许我应该看看其他任何人都可以提示的解决方案?

编辑澄清 今天,该解决方案获取所有违规,将它们发送到 webGrid 并让 webgrid 进行分页和排序。解决方案已经运行了很多年,违规表已经增长到页面非常慢的地步,主要是因为每次都获取所有数据。我已经实现了对存储库的分页以仅接收该类的一部分。今天的存储库与 IEnumerable 和表示层和存储库之间的 ServiceLayer(业务)一起工作,总是向表示层返回一个列表。

这是我希望能够使用的 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;
    }

最佳答案

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

首先是所有的辅助方法

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

我可以从 Controller 中调用它

        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" 
            );

关于c# - 具有复杂属性的 IEnumerable OrderBy by String,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32483114/

相关文章:

c# - 在 C# lambda 中捕获计数器

entity-framework - SQL逻辑错误或缺少数据库没有这样的功能: last_rows_affected

C# EF Deep Lambda 非重复计数查询

c# - 分组输出集合中的项目

c# - 迭代列表,但无法访问不同的项目值

domain-driven-design - IEnumerable<T>.ConvertAll & DDD

c# - 在 C#7 中,如何将 "roll my own"类任务类型与异步一起使用?

c# - 我如何使我的 gridview 100% 适合页面

C# 程序不会运行过去的对象实例

c# - 实体 - 两个连接表是什么类型