Dynamic Linq with Expression Trees

I recently came across a project requirement for the client to create custom validation rules for retrieving data from the SQL database. The rules needed be applied against any number of member applications, and can be altered by the client at any point.

From the client perspective, the administration of the rules would look something like the following:

Rules Administration

Simple enough concept.  Historically, given such a requirement, you’d likely end up with some fudged SQL string (if querying a database), and/or a whole lot of conditional logic.  The application already uses Linq2SQL, so I didn’t want to go backwards by introducing some extensive SQL string generation.

The first Linq based option I came across was the Dynamic Linq Query Library, which essentially enables you to use SQL-like strings as expressions in your conditional clauses.  Nice, but it still smelled a bit like the SQL string concatenation of old.

The second option I looked at was PredicateBuilder.  It’s a neat solution for the right problem, but it assumes that you know the properties/columns you’re querying against at compile time.

So, I decided to have a play around with Expression Trees, in an effort to dynamically generate my conditional expressions at runtime.  I hadn’t really delved much into this area before, so I figured it’d be a good learning experience, even if it didn’t end up working out.  To cut to the chase, the method I ended up with for parsing my rules into Lambda Expressions looks like the following:

public static Expression<Func<T, bool>> GetExpression<T>(
	string propertyName, string operatorType, string propertyValue)
{
	var isNegated = operatorType.StartsWith("!");
	if (isNegated)
		operatorType = operatorType.Substring(1);

	var parameter = Expression.Parameter(typeof (T), "type");
	var property = Expression.Property(parameter, propertyName);

	// Cast propertyValue to correct property type
	var td = TypeDescriptor.GetConverter(property.Type);
	var constantValue = Expression.Constant(td.ConvertFromString(propertyValue), property.Type);

	// Check if specified method is an Expression member
	var operatorMethod = typeof(Expression).GetMethod(operatorType, new[] { typeof(MemberExpression), typeof(ConstantExpression) });

	Expression expression;

	if (operatorMethod == null)
	{
		// Execute against type members
		var method = property.Type.GetMethod(operatorType, new[] {property.Type});
		expression = Expression.Call(property, method, constantValue);
	}
	else
	{
		// Execute the passed operator method (e.g. Expression.GreaterThan)
		expression = (Expression) operatorMethod.Invoke(null, new object[] {property, constantValue});
	}

	if (isNegated)
		expression = Expression.Not(expression);

	return Expression.Lambda<Func<T, bool>>(expression, parameter);
}

Which can be executed with a call like the following:

foreach (var rule in rules)
{
	applicants = applicants.Where(
		GetExpression<Applicant>(
			rule.MemberName, // The Applicant property
			rule.Operator, // Equals, Contains, GreaterThan, etc
			rule.Value)
		);
}

The rules are looped over, and Linq does its magic by combining all the separate rules with AND statements in the resulting query.

A few things to note:

  • Operators are just the method names to be called, against the Expression type, or any other CLR or custom type.  If you put a ! in front of the operator, it negates the method result (!Contains would read ‘Does Not Contain’).
  • Reflection isn’t necessary for building an expression, I just use it here to enable runtime operator implementation, against both the Expression class and potentially any other class.
  • If you wanted to combine your rules with OR statements, it could be done easily enough with logic similar to what PredicateBuilder uses.
  • There is possibly/probably better ways to do this – if you know one, speak up!

Is this necessarily any better than using the Dynamic Linq library?  A bit – it doesn’t really give me any compile-time type safety benefits, but I could potentially handle certain exceptions that I perhaps wouldn’t be able to detect using Dynamic Linq, such as casting the value to the correct type.  On top of that, I like it more, it feels more robust, and doesn’t require external libraries to work.  If nothing else, it gave me an excuse to get my head around some lower-level Expression Tree stuff :)

This entry was posted in Development and tagged , , . Bookmark the permalink.

8 Responses to Dynamic Linq with Expression Trees

  1. This is a cool demonstration of expression tree building. I have to do this sometime.

    I have almost the exact same requirement in my current project. My data is in arrays, so in a way its more straightforward.

    I created 2 filter types – 1 for enums and 1 for range filtering. Everything is bound to a WPF UI so when the user selects the field, they’re also selecting the index into the array (as a property of the Field type). Also, the user selects in a slider for ranges and a list for enums. In the UI you show, how do you select both London & NY. That doesn’t seem to work the code either.

    In the end, my filtering in Linq looks very similar to yours and the whole system is nearly logically identical.

  2. Marcus
    says:

    In the UI you show, how do you select both London & NY. That doesn’t seem to work the code either.

    That’s not yet a requirement, but if/when it is, I’d need to introduce some logic like PredicateBuilder in the back end, and some optional rule grouping by ‘OR/AND’ in the client UI to enable it.

  3. Shane says:

    I appreciate your example and it works great with non-nullable fields, however I am having fits trying to make this work with nullable fields. Any suggestions?

  4. Marcus
    says:

    I’m not sure what you mean Shane, it seems to work ok with nullable props too for me. Using that same method to create the expression, I just pass in a nullable property and it all works.

    Is there a specific example you can give?

  5. Shane says:

    I am using Entity Framework and have an entity with a nullable decimal field. When I attempt to call this method on that field I get this error:

    Expression of type ‘System.Nullable`1[System.Decimal]‘ cannot be used for parameter of type ‘System.Object’ of method ‘Boolean Equals(System.Object)’

    It appears to be related to the fact that EF cannot handle the Object parameter. I am not sure if this is just an issue with Linq to EF or not.

    As an example this will also give an error:

    decimal? testVal = new decimal?(2100);
    var test = (from i in context.Leases where i.AnnualRental.Equals(testVal) select i).ToList();

  6. Shane says:

    Interesting note:

    decimal? testVal = new decimal?(2100);
    var test = (from i in context.Leases where i.AnnualRental == testVal select i).ToList();

    the above works, while the following throws an error:

    decimal? testVal = new decimal?(2100);
    var test = (from i in context.Leases where i.AnnualRental.Equals(testVal) select i).ToList();

    Unable to create a constant value of type ‘System.Object’. Only primitive types (‘such as Int32, String, and Guid’) are supported in this context.
    Source : System.Data.Entity

  7. Shane says:

    If i use Equal instead of Equals for the operator type, everything works just fine.

  8. Jhelumi says:

    I need something like this for my Advance Search form. What I’m doing is passing the rules collection to a method which uses the foreach loop to apply all the where conditions to my object. my question is how I’m going to return the results.
    public IQueryable GetProducts(List rules)
    {
    foreach( var rule in rules_
    { var products = this.ObjectContext.Products.Where(GetExpression(
    rule.MemberName, // The Applicant property
    rule.Operator, // Equals, Contains, GreaterThan, etc
    rule.Value)
    );
    }
    return ??
    }

    How can I make the query to run and return the result

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>