Skip to content
IharBury edited this page Oct 7, 2014 · 5 revisions

Including lamdba expressions into each other

Problem

When writing regular methods and delegates via .NET it's easy to (re)use them from each other. Expressions however are not so easy. Lambda syntax doesn't allow you using one expression inside another one out of the box. If you use Compile method inside an expression to get a delegate that you may then call, such an expression will not be supported by most LINQ providers.

Solution

Mindbox.Expressions library allows you to include lambda expressions into each other and transform the result into a plain expression translatable to LINQ-to-SQL, Entity Framework or whatever you need.

First Step

Use Evaluate method to include expressions into each other:

Expression<Func<int, int>> squareExpression = x => x * x;
Expression<Func<int, int, int>> squareSumExpression = (x, y) => 
    squareExpression.Evaluate(x) + squareExpression.Evaluate(y);

Alternatively you can use Compile() method instead:

Expression<Func<int, int, int>> squareSumExpression = (x, y) =>
    squareExpression.Compile()(x) + squareExpression.Compile()(y);

Or even:

Expression<Func<int, int, int>> squareSumExpression = (x, y) => 
    squareExpression.Compile().Invoke(x) + 
    squareExpression.Compile().Invoke(y);

Second Step

Save the resulting expression to a variable and call ExpandExpressions() method on it:

Expression<Func<int, int, int>> squareSumExpression = (x, y) => 
    squareExpression.Evaluate(x) + squareExpression.Evaluate(y);
var expandedExpression = squareSumExpression.ExpandExpressions();

You get expanded expression containing just

(x, y) => (x * x) + (y * y);

Or better use it in IQueryable methods and call ExpandExpressions() method on resulting IQueryable<> before it gets enumerated (or translated in any other way):

IQueryable<Human> query = ...;
Expression<Func<Human, bool>> isGirlExpression = ...;
Expression<Func<Human, int>> ageExpression = ...;
var result = query
    .Where(human => isGirlExpression.Evaluate(human) && 
        (ageExpression.Evaluate(human) >= 18))
    .OrderBy(human => human.Name)
    .ExpandExpressions()
    .ToList();

Special Cases

Often you just need to combine boolean expressions with same parameters via AndAlso (&&) or OrElse (||). You can do it via extension methods:

Expression<Func<Human, bool>> isGirlExpression = ...;
var combinedViaAndAlso =
    isGirlExpression.AndAlso(human => human.Age >= 18);

Or if you have a non-empty collection of such expressions, you can combine them all at once:

IEnumerable<Expression<Func<Human, bool>>> expressions = ...;
var combinedViaAndAlso =
    BooleanExpressions.CombineViaAndAlso(expressions);

Referencing class members for use in reflection

Problem

Visual Studio and other code editors support automated refactorings allowing you, for example, to rename class members. However sometimes we do not reference class members in code directly but access or reference them via reflection by name. But names are just strings and automated tools cannot handle them.

Solution

ReflectionExpressions class allows you to reference class members by building an expression referencing them. Expressions are just code so the tools can handle them.

To get MethodInfo of an instance method (or an extension method):

class SomeClass
{
    string GetSomething(int x, double y);
}

var methodInfo = ReflectionExpressions.GetMethodInfo<SomeClass>(
    someClass => someClass.GetSomething(
        default(int), 
        default(double));

To get MethodInfo of a static method:

class SomeClass
{
    static string GetSomething(int x, double y);
}

var methodInfo = ReflectionExpressions.GetMethodInfo(
    () => SomeClass.GetSomething(
        default(int), 
        default(double));

If you need just a name, you can use GetMethodName methods. If you aren't sure the expression you use is valid, you can use TryGetMethodInfo and TryGetMethodName methods.

The same for properties and fields:

  • GetPropertyInfo
  • GetPropertyName
  • TryGetPropertyInfo
  • TryGetPropertyName
  • GetFieldInfo
  • GetFieldName
  • TryGetFieldInfo
  • TryGetFieldName