Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dynamically create a lambda expression

Tags:

c#

.net

lambda

Let's suppose I have the following class:

public class Show
{
    public string Language { get; set; }
    public string Name { get; set; }
}

Based on this information, my goal is to create a lambda expression like this:

g => g.Language == lang && g.Name == name

lang and name are local variables I would like to add as constant values when creating the expression.

As you can see, the compiled function would be of type Func<Genre, bool>

To help you understand more clearly, I would like to achieve something similar to this:

string lang = "en";
string name = "comedy";
Genre genre = new Genre { Language = "en", Name = "comedy" };
Expression<Func<Genre, bool>> expression = CreateExpression(genre, lang, name);
// expression = (g => g.Language == "en" && g.Name == "comedy")

I am aware of the existence of expression trees but I'm pretty much new to this topic so I don't even know how to start.

Can this problem be solved? How can I create such expression dynamically?

like image 368
Matias Cicero Avatar asked Jul 08 '15 01:07

Matias Cicero


Video Answer


1 Answers

public Expression<Func<TValue, bool>> CreateExpression<TValue, TCompare>(TValue value, TCompare compare)
{
    var pv = Expression.Parameter(typeof(TValue), "data");
    var compareProps = typeof(TCompare).GetProperties();

    // First statement of the expression
    Expression exp = Expression.Constant(true);

    foreach (var prop in typeof(TValue).GetProperties())
    {
        // Check if the compare type has the same property
        if (!compareProps.Any(i => i.Name == prop.Name))
            continue;

        // Build the expression: value.PropertyA == "A" 
        // which "A" come from compare.PropertyA
        var eq = Expression.Equal(
            Expression.Property(pv, prop.Name), 
            Expression.Constant(compareProps
                .Single(i => i.Name == prop.Name)
                .GetValue(compare)));

        // Append with the first (previous) statement
        exp = Expression.AndAlso(exp, eq);
    }

    return Expression.Lambda<Func<TValue, bool>>(exp, pv);
}

Usage:

var value = new { Lang = "en", Name = "comedy"};

// The compareValue should have the same property name as the value, 
// or the expression will just ignore the property
var compareValue = new { Lang = "en", Name = "comedy", Other = "xyz" };

// The create expression content is
// {data => ((True AndAlso (data.Lang == "en")) AndAlso (data.Name == "comedy"))}
bool isMatch = CreateExpression(value, compareValue).Compile()(value); // true
like image 166
Eric Avatar answered Oct 07 '22 20:10

Eric