【问题标题】:Expression for OrderBy linq with inner expression带有内部表达式的 OrderBy linq 表达式
【发布时间】:2013-02-20 18:18:52
【问题描述】:

我有以下排序表达式:

this.Students = this.Students.OrderBy(x => x.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(p => p.ExamId).FirstOrDefault().TotalMarks);

虽然我的想法是抽象表达式为

x => x.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(
                                  p => p.ExamId).FirstOrDefault().TotalMarks

制作成一个 lambda 表达式,这样我就可以使用 like

this.Students = this.Students.OrderBy(sortExpression);

这是因为我有很多排序字段,例如上面定义的 TotalMarks,我只想从排序字段创建表达式,然后调用 OrderBy。

我从这个link 知道,我们可以创建一个使用子属性的表达式,但不能使用内部表达式。

目前我已经给出了一个开关盒,并在每种情况下都写了相同的东西,比如

this.Students = this.Students.OrderBy(x => x.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(p => p.ExamId).FirstOrDefault().SubjectName);

所以我的想法是使用静态方法创建 kindof ExpressionBuilder,该方法构建在 fieldName 上传递的表达式,例如

public static Expression BuildSortExpression(string fieldName) {}

【问题讨论】:

    标签: c# linq lambda


    【解决方案1】:

    您可以轻松地将大部分逻辑提取到方法中:

    private int sortExpression(Student x) { 
        return x.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(p => p.ExamId).FirstOrDefault().TotalMarks;
    }
    

    假设TotalMarksint

    那么你只需要使用:

    this.Students.OrderBy(x => sortExpression(x));
    

    或将其添加为 Student 的属性。

    警告: 如果您将它与 ORM(linq to SQL、Entity 框架等)一起使用,它的执行效率将不如之前的代码!

    【讨论】:

    • 那不会被 EF 正确转换。
    • 好点,虽然问题中没有提到 EF,所以我认为它是一个直接可枚举的,但我会更新问题。
    • 他指的是Expression,它告诉我们这是一个IQueryable。可能是其他一些提供程序,例如 Linq to SQL,它会支持这一点,但 EF 至少是一个可能的选择。
    • 我完全同意,我只是没想到!你的可能是最好的答案(我已经投票了),我会留下我的,因为它确实提供了按 desc 订购的灵活性,如果需要的话。
    • 如果您需要降序排序,如果查询提供程序支持该操作,您可以调用Reverse。如果没有,您可以为 IsDecending 的辅助方法提供一个可选的布尔值。
    【解决方案2】:

    尝试创建一个可重用的表达式变量最终会比仅仅创建自己的扩展方法来完成整个排序需要更多的工作:

    public static IQueryable<Student> OrderByMarks(this IQueryable<Student> students)
    {
        return students.OrderBy(student => student.ExamData
            .OrderByDescending(exam => exam.ExamDate)
            .ThenBy(exam => exam.ExamId)
            .FirstOrDefault().TotalMarks);
    }
    

    那么你可以这样使用它:

    this.Students = this.Students.OrderByMarks();
    

    【讨论】:

    • 但这就像我现在正在做的一样,因为我必须为每个属性(字段名称)创建方法,这对我来说最不可能是重构时的选择。跨度>
    【解决方案3】:

    使用 Ben 的想法解决了这个问题。 创建了一个

    Dictionary<string, Func<Student, Object>>
    

    以排序字段为键,函数为

    new Func<Student, object>((Student student) => { return GetLatestExam(student).TotalMarks; })
    

    和GetLatestExam 静态方法一样

    private static Study GetLatestExam(Student student)
    {
         return student.ExamData.OrderByDescending(p => p.ExamDate).ThenByDescending(p => p.ExamId).FirstOrDefault();
    }
    

    然后在实际排序中,我只需要这样调用:

    public void Sort(string sortField, bool sortAscending)
    {
         // based on the sort field, get the sorting expression and execute.
         if(sortAscending)
         {
              this.Students= this.Students.OrderBy(student=>this._customSortColumns[sortField](student));
         }
         else
         {
              this.Patients = this.Patients.OrderByDescending(student=>this._customSortColumns[sortField](student));
         }
    }
    

    【讨论】:

    • 这是一个 LINQ to objects 解决方案,但 OP 正在使用查询提供程序。
    猜你喜欢
    • 1970-01-01
    • 2013-07-02
    • 1970-01-01
    • 2017-12-25
    • 2016-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多