Tutorial :C# refactoring lambda expressions



Question:

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.


Solution:1

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.


Solution:2

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");  


Solution:3

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!


Solution:4

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";  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »