基于数组生成动态LINQ表达式

Generate dynamic LINQ expression based on array

我正在使用 LINQ 表达式查询客户并按州名过滤他们。我有以下查询,在我的 statesArray 中有 4 个项目之前,它工作正常。

public void GetCustomersForSelectedStates(string[] statesArray)
{
    var customers = _repo.GetAllCustomers();
    var filteredCustomers = from CUST in customers
    join ST in States on CT.Tag_Id equals ST.Id                       
    where CUST.ID == customer.ID  && (ST.Name == statesArray[0] ||ST.Name ==statesArray[1] || ST.Name== statesArray[2]||ST.Name =statesArray[3])

    //Do something with customers

}

我想动态创建以下表达式:

(ST.Name == statesArray[0] ||ST.Name ==statesArray[1] ||    
 ST.Name== statesArray[2]||ST.Name =statesArray[3])

例如,创建如下所示的动态查询

var dynamicQuery = "(";
var dynamicQuery = "(";
for (int i = 0; i < statesArray.Count(); i++)
    {
        dynamicQuery += "ST.Name =="+statesArray[0];
        if(i==statesArray.Count())
        dynamicQuery+=")"
    }

然后像下面这样使用它,

//Psuedo code
var customers = _repo.GetAllCustomers();
    var filteredCustomers = from CUST in customers
    join ST in States on CT.Tag_Id equals ST.Id                       
    where CUST.ID == customer.ID  && Expression(dynamicQuery)

您可以添加另一个加入

var customers = _repo.GetAllCustomers();

var filteredCustomers = from CUST in customers
    join ST in States on CT.Tag_Id equals ST.Id  
    join SA in statesArray on ST.Name equals SA // No need dynamic expression now          
where CUST.ID == customer.ID 

//Do something with customers

要做到这一点通过动态表达式基本上意味着构建一棵树:

(x.Foo == val0 || x.Foo == val1 || x.Foo == val2)

你可以这样做:

static Expression<Func<T, bool>> Where<T, TVal>(Expression<Func<T, TVal>> selector,
    IEnumerable<TVal> values)
{
    Expression result = null;
    foreach (var val in values)
    {
        var match = Expression.Equal(
            selector.Body,
            Expression.Constant(val, typeof(TVal)));

        result = result == null ? match : Expression.OrElse(result, match);
    }
    if (result == null) return x => true; // always match if no inputs

    return Expression.Lambda<Func<T, bool>>(result, selector.Parameters);
}

使用示例:

string[] names = { "a", "c" };
var predicate = Where<Customer, string>(c => c.Name, names);

然后您可以在 IQueryable<T>.Where 扩展方法中使用此 predicate

要结合您的情况,首先执行您的常规 LINQ:

var customers = _repo.GetAllCustomers();
var filteredCustomers = from CUST in customers
join ST in States on CT.Tag_Id equals ST.Id                       
where CUST.ID == customer.ID;

现在作为一个单独的步骤应用额外的过滤器:

customers = customers.Where(predicate);

它所做的是接受 input 形式的 lambda c => c.Name,然后重用 c.Name body 对于每个 c.Name == {val},并重新使用 c 参数 作为我们正在创建的 lambda 的参数。 values 中的每个值都成为类型常量。 Expression.Equal 给了我们 == 测试。 OrElse||,注意如果resultnull,这是第一个项,所以我们只用匹配表达式本身。