简体   繁体   English

如何将表达式传递给实体框架LINQ查询OrderBy子句

[英]How to pass an Expression into Entity Framework LINQ query OrderBy clause

I have the following LINQ query: 我有以下LINQ查询:

using (var context = new EUContext())
        {
            var tmp = context.Terms.Include(x => x.StudentCourses)
                .Where(x => x.StudentID == studentId && x.DepartmentID == departmentId)
                .OrderBy(x => x.AcademicYear)
                .ThenBy(x=> x.TermRegistered == "Fall" ? 1 :
                            x.TermRegistered == "Spring" ? 2 : 3));

            return tmp.ToList();
        }

I am trying to move the OrdyBy in the ThenBy clause to clean up the code. 我试图在ThenBy子句中移动OrdyBy来清理代码。 I am trying to use an expression as following: 我试图使用如下表达式:

private static Expression<Func<string, int>> TermsOrder(string x)
        {
            return (x == "Fall" ? 1 :
                    x == "Spring" ? 2 : 3);
        }

and my code should look like this: 我的代码应如下所示:

using (var context = new EUContext())
            {
                var tmp = context.Terms.Include(x => x.StudentCourses)
                    .Where(x => x.StudentID == studentId && x.DepartmentID == departmentId)
                    .OrderBy(x => x.AcademicYear)
                    .ThenBy(x=> TermsOrder(x.TermRegistered));

                return tmp.ToList();
            }

Unfortunately the expression doesn't work there is a long squiggly line in the body of the expression with the following error message: 不幸的是,表达式不起作用表达式正文中有一条长曲线,并带有以下错误消息:

Cannot implicitly convert type 'int' to 'System.Linq.Expressions.Expression> 无法将类型'int'隐式转换为'System.Linq.Expressions.Expression>

What am I doing wrong? 我究竟做错了什么? This is my first try on using expressions and I know that I am missing something obvious due to not fully understanding how expressions work. 这是我第一次尝试使用表达式,我知道由于没有完全理解表达式如何工作,我遗漏了一些明显的东西。

Thanks 谢谢

This isn't as simple as it seems. 这并不像看起来那么简单。 You need to combine Expression s or build Expression s to generate what you want, and unfortunately C# doesn't include a lot of help in that area. 你需要结合Expression或构建Expression来生成你想要的东西,不幸的是C#在这方面没有很多帮助。

The easiest approach is to use an extension method for LambdaExpression composition. 最简单的方法是使用LambdaExpression组合的扩展方法。 It depends on some Expression extension methods for replacing one Expression with another in an Expression : 它依赖于一些Expression扩展方法用于替换一个Expression中的另一个Expression

public static class ExpressionExt {
    // Compose: f.Compose(g) => x => f(g(x))
    /// <summary>
    /// Composes two LambdaExpression into a new LambdaExpression: f.Compose(g) => x => f(g(x))
    /// </summary>
    /// <param name="fFn">The outer LambdaExpression.</param>
    /// <param name="gFn">The inner LambdaExpression.</param>
    /// <returns>LambdaExpression representing outer composed with inner</returns>
    public static Expression<Func<T, TResult>> Compose<T, TIntermediate, TResult>(this Expression<Func<TIntermediate, TResult>> fFn, Expression<Func<T, TIntermediate>> gFn) =>
        Expression.Lambda<Func<T, TResult>>(fFn.Body.Replace(fFn.Parameters[0], gFn.Body), gFn.Parameters[0]);    

    /// <summary>
    /// Replaces a sub-Expression with another Expression inside an Expression
    /// </summary>
    /// <param name="orig">The original Expression.</param>
    /// <param name="from">The from Expression.</param>
    /// <param name="to">The to Expression.</param>
    /// <returns>Expression with all occurrences of from replaced with to</returns>
    public static Expression Replace(this Expression orig, Expression from, Expression to) => new ReplaceVisitor(from, to).Visit(orig);
}

/// <summary>
/// Standard ExpressionVisitor to replace an Expression with another in an Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
    readonly Expression from;
    readonly Expression to;

    public ReplaceVisitor(Expression from, Expression to) {
        this.from = from;
        this.to = to;
    }

    public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}

Now you can create your method that takes a lambda representing the field you want to test. 现在,您可以创建一个方法,该方法采用代表您要测试的字段的lambda。 It uses a local LambdaExpression as a template for the final result: 它使用本地LambdaExpression作为最终结果的模板:

public static class Util {
    static Expression<Func<string, int>> TermOrderTemplateFn = p => (p == "Fall" ? 1 : p == "Spring" ? 2 : 3);
    public static Expression<Func<TRec, int>> TermsOrder<TRec>(Expression<Func<TRec, string>> selectorFn) =>
        TermOrderTemplateFn.Compose(selectorFn);
}

Now you can call the method in your expression, passing in a lambda representing the desired field (or field expression) to test: 现在,您可以在表达式中调用该方法,传入表示所需字段(或字段表达式)的lambda来测试:

var tmp = context.Terms.Include(x => x.StudentCourses).AsQueryable()
                .Where(x => x.StudentID == studentId && x.DepartmentID == departmentId)
                .OrderBy(x => x.AcademicYear)
                .ThenBy(Util.TermsOrder<Term>(p => p.TermRegistered));

Note: I am calling the type of context.Terms.First() Term but you would need to use the actual correct type name in the call to TermsOrder . 注意:我正在调用context.Terms.First() Term的类型,但您需要在调用TermsOrder使用实际的正确类型名称。 You could also do TermsOrder((Term p) => ...) instead. 您也可以使用TermsOrder((Term p) => ...)

I would probably prefer to create a special version of ThenBy so you can use type inference to determine the record type: 我可能更喜欢创建一个特殊版本的ThenBy以便您可以使用类型推断来确定记录类型:

public static class EFExt {
    static Expression<Func<string, int>> TermThenOrderTemplateFn = p => (p == "Fall" ? 1 : p == "Spring" ? 2 : 3);
    public static IOrderedQueryable<T> ThenByTerm<T>(this IOrderedQueryable<T> src, Expression<Func<T, string>> selectorFn) =>
        src.ThenBy(TermThenOrderTemplateFn.Compose(selectorFn));
}

Then you can use it directly: 然后你可以直接使用它:

var tmp = context.Terms.Include(x => x.StudentCourses).AsQueryable()
                .Where(x => x.StudentID == studentId && x.DepartmentID == departmentId)
                .OrderBy(x => x.AcademicYear)
                .ThenByTerm(p => p.TermRegistered);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM