如何在c#中为Contains动态创建linq表达式

时间:2018-02-27 07:34:00

标签: linq reflection lambda entity-framework-core

我试图动态创建表达但我无法弄明白......也不想像这样使用..

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项目和归档ID,它们的值来自IQueryCollection转换键和值以使用实体框架查询mysql ... for 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);
    }
}

2 个答案:

答案 0 :(得分:1)

假设您处理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()));

答案 1 :(得分:1)

感谢您的回答是对的,我的结果如下。对于&gt; =并包含过滤器..我用于具有实体框架的查询数据库..

    // >=  
    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);