C# 动态构建 Linq Lambda 表达式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2297502/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 01:11:58  来源:igfitidea点击:

Dynamically Build Linq Lambda Expression

c#lambda

提问by Dave

Okay, my guess is this is answered somewhere already, and I'm just not quite familiar enough with the syntax yet to understand, so bear with me.

好吧,我的猜测是这已经在某处得到了回答,而且我对语法还不够熟悉,尚无法理解,所以请耐心等待。

The users of my web app need to filter a long list of items in a gridview, accessed via a linqdatasource. I am using the OnSelecting Event to further filter items. I want to filter those items based on selections the users make in DropDownLists.

我的 web 应用程序的用户需要过滤 gridview 中的一长串项目,通过 linqdatasource 访问。我正在使用 OnSelecting 事件来进一步过滤项目。我想根据用户在 DropDownLists 中所做的选择来过滤这些项目。

For example, they select "Title" "Contains" "Fred" This results in

例如,他们选择“标题”“包含”“弗雷德”这将导致

e.Result = dbContext.Opps.Where(opp => opp.Title.Contains("Fred"));

Or "Description" "Does not Contain" "Alpha" results in

或“描述”“不包含”“Alpha”结果

 e.Result = dbContext.Opps.Where(opp => !opp.Description.Contains("Alpha"));

I would like to build up that Expression (System.Linq.Expressions.Expression>) dynamically, instead of having nested switch expressions to generate it, as there are a number of fields I want to check, and I also want to use the StartsWith and EndsWith checks. If I could build the Expression as a string, like so:

我想动态地构建该表达式 (System.Linq.Expressions.Expression>),而不是使用嵌套的 switch 表达式来生成它,因为我想检查许多字段,而且我还想使用 StartsWith和 EndsWith 检查。如果我可以将表达式构建为字符串,如下所示:

string stringExpression = string.Format("opp => opp.{0}.{1}(\"{2}\")",
    ddlCustomFilter.SelectedValue,
    ddlFilterType.SelectedValue,
    txtFilterText.Text);

And then somehow have it be converted into an Expression... is this possible? Or should I just bite the bullet and generate all the switch() statements required to create the various expressions?

然后以某种方式将其转换为表达式......这可能吗?或者我应该硬着头皮生成创建各种表达式所需的所有 switch() 语句?

采纳答案by tvanfosson

You could certainly build the expression dynamically, but I would consider using Dynamic LINQas an alternative first, though you may not be able to use Contains. In addition, you may want to consider using PredicateBuilderto build complex queries additively.

您当然可以动态构建表达式,但我会首先考虑使用动态 LINQ作为替代方案,尽管您可能无法使用包含。此外,您可能需要考虑使用PredicateBuilder以附加方式构建复杂查询。

回答by suneelsarraf

try this code...

试试这个代码...

call ToExpression Method()....

调用 ToExpression Method()....

public static Expression<Func<T, bool>> ToExpression<T>(string andOrOperator, string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
    {
        Expression<Func<T, bool>> func = null;
        try
        {
            ParameterExpression paramExpr = Expression.Parameter(typeof(T));
            var arrProp = propName.Split('.').ToList();
            Expression binExpr = null;
            string partName = string.Empty;
            arrProp.ForEach(x =>
            {
                Expression tempExpr = null;
                partName = partName.IsNull() ? x : partName + "." + x;
                if (partName == propName)
                {
                    var member = NestedExprProp(paramExpr, partName);
                    var type = member.Type.Name == "Nullable`1" ? Nullable.GetUnderlyingType(member.Type) : member.Type;
                    tempExpr = ApplyFilter(opr, member, Expression.Convert(ToExprConstant(type, value), member.Type));
                }
                else
                    tempExpr = ApplyFilter("!=", NestedExprProp(paramExpr, partName), Expression.Constant(null));
                if (binExpr != null)
                    binExpr = Expression.AndAlso(binExpr, tempExpr);
                else
                    binExpr = tempExpr;
            });
            Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(binExpr, paramExpr);
            if (expr != null)
                innerExpr = (andOrOperator.IsNull() || andOrOperator == "And" || andOrOperator == "AND" || andOrOperator == "&&") ? innerExpr.And(expr) : innerExpr.Or(expr);
            func = innerExpr;
        }
        catch { }
        return func;
    }

    private static MemberExpression NestedExprProp(Expression expr, string propName)
    {
        string[] arrProp = propName.Split('.');
        int arrPropCount = arrProp.Length;
        return (arrPropCount > 1) ? Expression.Property(NestedExprProp(expr, arrProp.Take(arrPropCount - 1).Aggregate((a, i) => a + "." + i)), arrProp[arrPropCount - 1]) : Expression.Property(expr, propName);
    }

    private static Expression ToExprConstant(Type prop, string value)
    {
        if (value.IsNull())
            return Expression.Constant(value);
        object val = null;
        switch (prop.FullName)
        {
            case "System.Guid":
                val = value.ToGuid();
                break;
            default:
                val = Convert.ChangeType(value, Type.GetType(prop.FullName));
                break;
        }
        return Expression.Constant(val);
    }

    private static Expression ApplyFilter(string opr, Expression left, Expression right)
    {
        Expression InnerLambda = null;
        switch (opr)
        {
            case "==":
            case "=":
                InnerLambda = Expression.Equal(left, right);
                break;
            case "<":
                InnerLambda = Expression.LessThan(left, right);
                break;
            case ">":
                InnerLambda = Expression.GreaterThan(left, right);
                break;
            case ">=":
                InnerLambda = Expression.GreaterThanOrEqual(left, right);
                break;
            case "<=":
                InnerLambda = Expression.LessThanOrEqual(left, right);
                break;
            case "!=":
                InnerLambda = Expression.NotEqual(left, right);
                break;
            case "&&":
                InnerLambda = Expression.And(left, right);
                break;
            case "||":
                InnerLambda = Expression.Or(left, right);
                break;
            case "LIKE":
                InnerLambda = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right);
                break;
            case "NOTLIKE":
                InnerLambda = Expression.Not(Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right));
                break;
        }
        return InnerLambda;
    }

    public static Expression<Func<T, object>> PropExpr<T>(string PropName)
    {
        ParameterExpression paramExpr = Expression.Parameter(typeof(T));
        var tempExpr = Extentions.NestedExprProp(paramExpr, PropName);
        return Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Lambda(tempExpr, paramExpr).Body, typeof(object)), paramExpr);

    }
    public static IQueryOver<T, T> OrderBy<T>(this IQueryOver<T, T> Collection, string sidx, string sord)
    {
        return sord == "asc" ? Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Asc : Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Desc;
    }

    public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
    }