views:

279

answers:

4

I have several Expression<Func<User,bool>> expressions that shares properties. For example,

Expression<Func<User, bool>> e1 = 
  (User u) => u.IsActive && u.Group != "PROCESS" && u.Name != null;
Expression<Func<User, bool>> e2 = 
  (User u) => u.IsActive && u.Group != "PROCESS" && u.Name != "A";
Expression<Func<User, bool>> e3 = 
  (User u) => u.IsActive && u.Group != "PROCESS" && u.Name != "B";

Is there an easy way to put the u.IsActive && u.Group != "PROCESS" in a variable and use it in e1, e2 and e3? Edited : And I still want the same tree.

Seems I can do it by building the expression with Expression.Lambda<Func<User, bool>>(BinaryExpression.AndAlso( etc... But rather than simplifying my code it made it more difficult to read.

+1  A: 
var test = new Func<User, bool>(u=> u.IsActive && u.Group != "PROCESS");
Expression<Func<User, bool>> e1 = (User u) => test(u) && u.Name != null;
Expression<Func<User, bool>> e2 = (User u) => test(u) && u.Name != "A";
Expression<Func<User, bool>> e3 = (User u) => test(u) && u.Name != "B";
MKing
It works but I still want the complete tree (question edited).
Toto
-1. This is not equivalent to what OP wants. An expression tree is fundamentally different from an anonymous method. Both can be represented by a lambda bus shouldn't be confused.
Mehrdad Afshari
Excellent point and fair down vote, thanks for pointing it out.
MKing
I suppose it'd be handy to know why the OP is keen to keep this as an expression tree - this is the solution I'd naturally use, and I'm curious to know why it wouldn't work in your circumstance.
Jon Artus
@Jon I am using the expression as filters and want to display the filter and use the ToString() method of the expression to do it in order to be sure the filters are what I want. If I display a call to test(), I don't know whatthe test method filters (sure I can still display the test method if I use it as an expression tree but it's not perfect).
Toto
Jon: A simple example is LINQ to SQL. You can't pass an expression constructed this way to LINQ to SQL and expect it to work. There are plenty of other cases this applies.
Mehrdad Afshari
+3  A: 

I believe there's no cleaner way to do that for your case. You can use BinaryExpression as you mentioned. You can encapsulate the BinaryExpression and Expression.Lambda calls into a method and call that instead (like PredicateBuilder.And) but none of those are as clean as the current syntax IMO.

Mehrdad Afshari
+2  A: 

I don't think that there's necessarily a better answer than the one which you're using already. As Mehrdad mentions, you'll have to build a deeper tree using a BinaryExpression and I think that would be a step backwards in readability from your current code.

Depending on your usage, you might be able to save some lines of code by exploiting closure semantics and doing something like this:

string name = null;

Expression<Func<User, bool>> e = 
  (User u) => u.IsActive && u.Group != "PROCESS" && u.Name != name;

var expr = e.Compile();

name = "A";
var result = expr.Invoke(u); //True (assume u.Name = "B")

name = "B";
result = expr.Invoke(u); //False

...but whether that's any use will depend on what you're doing with the compiled delegate. May be entirely useless to you but worth a mention just in case!

Jon Artus
+1  A: 

Problem with lambda expressions is that they are immutable and you can't easily replace parameters of lambda. My original idea was to do something like this (unfortunately, this won't work out):

public static class ExpressionExtesions
{
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> baseCondition, Expression<Func<T, bool>> additionalCondition)
    {
        var and = Expression.AndAlso(baseCondition.Body, additionalCondition.Body);
        return Expression.Lambda<Func<T, bool>>(and, baseCondition.Parameters);  // additionalCondition.Body still uses its own parameters so this fails on Compile()
    }
}

and use in code:

Expression<Func<User, bool>> e = usr => usr.IsActive && usr.Group != "PROCESS";

var e1 = e.And(u => u.Name != null);
var e2 = e.And(u => u.Name != "A");
var e3 = e.And(u => u.Name != "B");

Possible solution

You can try to use one of the project aiming on implementing expression builders. I haven't used any of them but google gives plenty of links, for example:

Another approach

If you are using these expressions in LINQ to filter values, you can user different approach (don't combine expressions but combine filters):

var activeUsers = allUsers.Where(usr => usr.IsActive && usr.Group != "PROCESS");

var usersAll = activeUsers.Where(u => u.Name != null);
var usersNotA = activeUsers.Where(u => u.Name != "A");
var usersNotB = activeUsers.Where(u => u.Name != "B");
Konstantin Spirin
Yeah this will do the job too but this separates the filter. I want 3 filters with a common base. I would really like that your first idea works :p
Toto