【问题标题】:How To create linq Expression dynamically in c# for Contains如何在 C# 中为包含动态创建 linq 表达式
【发布时间】:2018-02-27 07:34:00
【问题描述】:

我正在尝试动态创建表达式,但我无法弄清楚...也不想这样使用..

if (item.id == 1) filter.AddExpression(c => filterValues.Contains(c.a1));
if (item.id == 2) filter.AddExpression(c => filterValues.Contains(c.a2));
if (item.id == 3) filter.AddExpression(c => filterValues.Contains(c.a3));
if (item.id == 4) filter.AddExpression(c => filterValues.Contains(c.a4));

...

var filtervalues = new List<int>(){1,2,3,4...}
Filter<TestSubject> filter = new Filter<TestSubject>(null);

如何通过迭代实现这一点...这是一个 Web 项目,并提交了 ids 和它们的值来自 IQueryCollection 转换键和值以使用实体框架查询 mysql...对于 Where in 子句试图做这。如果存在另一种方式表示赞赏,但现在我有这种强大的 :) 方式并且无法完成......

foreach (var field in QueryFields)
{           
    filter.AddExpression(c => filterValues.Contains(c=>"a"+field.id)); // Trying to do this
}

public class TestSubject 
{
    public int? a1 { get; set; }
    public int? a2 { get; set; }
    public int? a3 { get; set; }
    public int? a4 { get; set; }               
}

public class Filter<TEntity> 
{  
    public Filter(Expression<Func<TEntity, bool>> expression) 
    { 
        Expression = expression; 
    }

    public Expression<Func<TEntity, bool>> Expression { get; private set; }

    public void AddExpression(Expression<Func<TEntity, bool>> newExpression)
    {
        if (newExpression == null) throw new ArgumentNullException(nameof(newExpression), $"{nameof(newExpression)} is null.");

        if (Expression == null) Expression = newExpression;

        var parameter = System.Linq.Expressions.Expression.Parameter(typeof(TEntity));

        var leftVisitor = new ReplaceExpressionVisitor(newExpression.Parameters[0], parameter);
        var left = leftVisitor.Visit(newExpression.Body);

        var rightVisitor = new ReplaceExpressionVisitor(Expression.Parameters[0], parameter);
        var right = rightVisitor.Visit(Expression.Body);

        Expression = System.Linq.Expressions.Expression.Lambda<Func<TEntity, bool>>(System.Linq.Expressions.Expression.AndAlso(left, right), parameter);
    }
}

【问题讨论】:

  • Contains 不需要 lambda? QueryFields 的类型是什么?你看过 LINQKit,特别是 PredicateBuilder 吗?
  • 您的代码无法编译,因为您不能在List&lt;int&gt; 上使用int? 调用Contains
  • 是的,int 和 int?问题出现了,Expression.GreaterThanOrEqual 也有问题,所以需要使用 Expression.Convert.. 谢谢

标签: linq reflection lambda entity-framework-core


【解决方案1】:

感谢您的回答是正确的,我的结果如下所示。对于 >= 和包含过滤器.. 我使用实体框架查询数据库..

    // >=  
    var parameter = Expression.Parameter(typeof(Cx), "Cx"); // Cx Type.
    var member    = Expression.Property(parameter, "a" + fieldNo); //Cx.ay
    var constant  = Expression.Constant(int.Parse(query[fieldNo.ToString()]));
    Expression body     = Expression.GreaterThanOrEqual(member, Expression.Convert(Expression.Constant(int.Parse(query[.ToString()])), member.Type)); // Contains Conversion for GreaterThanOrEqual not accept int , int?
    var finalExpression = Expression.Lambda<Func<Classified, bool>>(body, parameter); 
    filter.AddExpression(finalExpression);           

    // Contains
    var methodInfo      = typeof(List<int?>).GetMethod("Contains", new Type[] { typeof(int?) }); // Contains Method
    var parameter       = Expression.Parameter(typeof(Cx), "Cx"); // Cx Type.
    var member          = Expression.Property(parameter, "a" + fieldNo); //Cx.ay
    var constant        = Expression.Constant(filterValues);
    Expression body     = Expression.Call(Expression.Constant(filterValues), methodInfo, member);
    var finalExpression = Expression.Lambda<Func<Classified, bool>>(body, parameter);
    filter.AddExpression(finalExpression);

【讨论】:

  • 人们在成功解决问题后很少发布最终答案。为此+1!它对我帮助很大。
【解决方案2】:

假设您处理 filterValuesTestSubject 属性之间的类型不匹配(intint?),您可以使用辅助函数:

public static Expression<Func<TEntity, bool>> MakeContainsLambda<TEntity, TTest>(Expression<Func<ICollection<TTest>>> valsref, string fieldPrefix, string fieldPostfix) {
    var param = Expression.Parameter(typeof(TEntity));
    var vals = valsref.Body;
    var miContains = valsref.Body.Type.GetMethod("Contains", new[] { typeof(TTest) });
    var field = Expression.PropertyOrField(param, fieldPrefix+fieldPostfix);
    var body = Expression.Call(vals, miContains, field);
    return (Expression<Func<TEntity, bool>>) Expression.Lambda(body, param);
}

创建所需的表达式:

filter.AddExpression(MakeContainsLambda<TestSubject, int>(() => filterValues, "a", item.id.ToString()));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 2015-11-29
    • 1970-01-01
    • 1970-01-01
    • 2019-03-31
    • 1970-01-01
    相关资源
    最近更新 更多