我想構建一個表達式樹,我可以將它們饋入Linq2SQL,以便它會生成一個很好的乾淨的查詢。我的目的是建立一個過濾器,將任意一組單詞與AND和NOT(或OR和NOT)放在一起。因爲我想改變我搜索的字段,所以我最好通過調用各種幫助函數將Expresssion<Func<T, string, bool>>
的列表組合在一起(其中T是我正在操作的實體)。然後,我會收到一組單詞並通過它們進行循環,然後構建一個Expresssion<Func<T, bool>>
(在必要時否定某些表達式),最終可以將它們提供給.Where語句。C中的currying表達式#
我一直在使用LINQKit PredicateBuilder,但是這段代碼處理單個參數表達式。但是,它爲我自己的嘗試提供了一些基礎。我的目標做這樣的事情:
var e = (Expression<Func<Entity, string, bool>>)((p, w) => p.SomeField.ToLower().Contains(w));
var words = new []{"amanda", "bob"};
var expr = (Expression<Func<Entity, bool>>)(p => false);
// building up an OR query
foreach(var w in words) {
var w1 = w;
>>>>expr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(expr.Body, (Expression<Func<Entity, bool>>)(p => e(p, w))));
}
var filteredEntities = table.Where(expr);
但因爲我使用的表達式通過>>>>標線顯然是非法的(不能做e(p, w)
我就像一個函數)。所以我的問題是如何將單個變量(單詞)部分應用於包含具有多個參數的函數的表達式?
好的,在LINQPad里弄了一下,弄清楚了一個適用於我的解決方案。 This question讓我在那裏。我對構建表達樹非常新,所以我會欣賞(並且讚揚)任何帶有改進或批評的評論/回答。
// Some set of expressions to test against
var expressions = new List<Expression<Func<Entity, string, bool>>>();
expressions.Add((p, w) => p.FirstName.ToLower().Contains(w));
expressions.Add((p, w) => p.LastName.ToLower().Contains(w));
expressions.Add((p, w) => p.Department != null && p.Department.Name.ToLower().Contains(w));
var words = new []{"amanda", "bob"};
var negs = new []{"smith"}; // exclude any entries including these words
var isAndQuery = true; // negate for an OR query
Expression<Func<Entity, bool>> posExpr = p => isAndQuery;
var entityParameter = Expression.Parameter(typeof(Entity), null);
// Build up the NOTs
var negExpr = (Expression<Func<Entity, bool>>)(p => true);
foreach(var w in negs) {
var w1 = w;
foreach(var e in expressions) {
var andNot = Expression.Invoke(e, entityParameter, Expression.Constant(w1));
negExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(negExpr.Body, Expression.Not(andNot)), entityParameter);
}
}
// Build up the ANDs or ORs
foreach(var w in words) {
var w1 = w;
var orExpr = (Expression<Func<Entity, bool>>)(p => false);
foreach(var e in expressions) {
var orElse = Expression.Invoke(e, entityParameter, Expression.Constant(w1));
orExpr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(orExpr.Body, orElse), entityParameter);
}
var orInvoked = Expression.Invoke(orExpr, posExpr.Parameters.Cast<Expression>());
if(isAndQuery)
posExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(posExpr.Body, orInvoked), entityParameter);
else
posExpr = Expression.Lambda<Func<Entity, bool>>(Expression.OrElse(posExpr.Body, orInvoked), entityParameter);
}
var posInvoked = Expression.Invoke(posExpr, posExpr.Parameters.Cast<Expression>());
var finalExpr = Expression.Lambda<Func<Entity, bool>>(Expression.AndAlso(negExpr.Body, posInvoked), entityParameter);
var filteredEntities = entities.Where(finalExpr);
我敢肯定,你見過http://stackoverflow.com/questions/3657843/linq-to-sql-query-help-string-contains-any-string-in-string-array已經但只是以防萬一你沒有。 – 2011-04-12 06:41:33