Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build a dynamic where clause over multiple properties

Tags:

c#

lambda

linq

What I have is a List<string> IndexFields which contains a list of property names.

My issue is that I need to build a where clause based on the elements in the list.

So far I have;

var sitem = List1.Where(p => (p.GetType().GetProperty(IndexFields[0])
  .GetValue(p, null) as string) == "red").FirstOrDefault();

But that only allows me to specify a single property. What I need is a builder that can build based on all the names in the List<string> IndexFields list.

like image 686
griegs Avatar asked Nov 25 '25 01:11

griegs


2 Answers

The most flexible way to create dynamic queries at runtime is by using the Expression API:

For example:-

var type = typeof(T);
var properties = IndexFields.Select(x => type.GetProperty(x));

// x
var paramter = Expression.Parameter(type);

// x.Foo, x.Bar, ...
var leftHandSides = properties.Select(
        x => Expression.Property(parameter, x));

// "Baz"
var rightHandSide = Expression.Constant(...);

// x.Foo == "Baz", x.Bar = "Baz", ...
var equalityExpressions = leftHandSides.Select(
        x => Expression.Equal(x, rightHandSide));

// x.Foo == "Baz" && x.Bar == "Baz" && ...
var aggregatedExpressions = equalityExpressions.Aggregate(
        (x, y) => Expression.AndAlso(x, y));

// x => x.Foo == "Baz" && x.Bar == "Baz" && ...
var lambda = Expression.Lambda<Func<T,bool>>(
        aggregatedExpressions, parameter)

var item = List1.Where(lambda).FirstOrDefault();

A huge advantage of building your queries like this is that the resulting expression can still be e.g. translated into SQL for use with Entity Framework, wheras using reflection inside the body of your lambda is really limiting.

I really do recommend taking some time to really understand the expression framework before using it, though. If you can grok what's going on, it saves you a ton of time in the long run.

You can read more at e.g:-

  • http://www.digitallycreated.net/Blog/37/dynamic-queries-in-entity-framework-using-expression-trees
  • https://stackoverflow.com/questions/1217539/net-expression-trees-tutorial

If you're looking for something quicker and dirtier, however, you can just go ahead and chain up those Where clauses inside a foreach:-

IEnumerable<T> query = List1;

foreach (var property in IndexFields)
{
  // The variable "property" gets hoisted out of local context
  // which messes you up if the query is being evaluated with
  // delayed execution.

  // If you're working in C# 6 though, you don't need to do this.
  var localProperty = property;

  query = query.Where(
    p => (p.GetType().GetProperty(localProperty)
                     .GetValue(p, null) as string) == "red");
}

var sitem = query.FirstOrDefault();
like image 88
Iain Galloway Avatar answered Nov 26 '25 15:11

Iain Galloway


You can use a PredicateBuilder for this:

var predicate = PredicateBuilder.New<string>();
 if (aCondition)
 {
  predicate = predicate.And(s => s == "this");
 }

 if (bCondition)
 {
  predicate = predicate.And(s => s == "that");
 }
like image 45
hutchonoid Avatar answered Nov 26 '25 15:11

hutchonoid