P.Brian.Mackey
P.Brian.Mackey

Reputation: 44295

Dynamically generate LINQ queries

We have an object

public class SomeObject
{
   public Name {get;set;}
   public City {get;set;}
   public State {get;set}
   //various other parameters.  Let's say there's ~20
}

Is it possible to dynamically create new LINQ queries without recompilation of source code? Instead, the query parameters come from an XML structure that is stored and updated in the database.

var result = from i in someObj
             where 
             //XML requests Name = 'Bob'...so append this where clause
             name = 'Bob'

Can this be done?

Upvotes: 47

Views: 125338

Answers (7)

Antineutrino
Antineutrino

Reputation: 1093

Maybe Dynamic Linq can help you: Dynamic linq part 1: Using the linq dynamic query library

query = query.Where("Id = 123 And Age > 18");

Or you can manipulate your Linq query directly:

query = query.Where(x=>x.Id == 5);

Upvotes: 9

Balazs Tihanyi
Balazs Tihanyi

Reputation: 6719

Here is a solution with expression trees:

var param = Expression.Parameter(typeof(SomeObject), "p");
var exp = Expression.Lambda<Func<SomeObject, bool>>(
    Expression.Equal(
        Expression.Property(param, "Name"),
        Expression.Constant("Bob")
    ),
    param
);
var query = someObj.Where(exp);

I know it's much more complex, but this may be useful in times.

Upvotes: 151

Steve Wortham
Steve Wortham

Reputation: 22260

It's hard for me to tell based on your question, but in some cases you don't need dynamic Linq and can simply do this...

var result = from o in someObj 
             where (Name == null || o.Name == Name)
             && (City == null || o.City == City)
             && (State == null || o.State == State)
             select o;

This will essentially prevent the data from being filtered when the parameter in question is null. And it still performs well thanks to the short-circuiting behavior in C#.

Upvotes: 36

Justin Pihony
Justin Pihony

Reputation: 67135

I believe you will have to actually dig into Expression Trees. I have not dug very far into this, so I cannot create a sample for you, but I do know that you can use Expression Trees to dynamically build your queries and then call .Compile (in the code) to have it runnable.

Actually, here is a better link Building Dynamic Queries with Expression Trees. It should give you exactly what you want, and is fairly succinct for what it is. This should act as a good example for you :)

Upvotes: 9

StriplingWarrior
StriplingWarrior

Reputation: 156748

Yes, it's actually pretty easy:

var name = GetBobNameFromXml();
var result = someObj.Where(i => i.Name == name);

You can also choose whether or not to apply criteria piecemeal.

var result = someObj;
var name = xmlCriteria.Name;
if(!string.IsNullOrEmpty(name))
{
    result = result.Where(i => i.Name == name);
}
// follow the same pattern for city, state, etc.

You could even use a pattern that uses a name-keyed dictionary of criterion funcs, to avoid a bunch of if statements.

foreach(var criterionPair in xmlCriteria)
{
    var value = criterionPair.Value;
    result = result.Where(i => propGetters[criterionPair.PropertyName](i, value));
}

Basically, there's a lot you can do along these lines. If you want an answer more specifically tailored to your situation, you'll need to provide a more specific question.

Upvotes: 5

Joachim Isaksson
Joachim Isaksson

Reputation: 181087

You'll most certainly want to take a look at Dynamic Linq which will allow you to define the query conditions as text.

As for adding conditions dynamically, you can add conditions to a query using similar syntax to;

if(CategoryIsImportant)
    myQuery = myQuery.Where("CategoryId=2");

all of which you can (fairly easily) encode into an XML format of your choice.

Upvotes: 30

Douglas
Douglas

Reputation: 54927

I assume you want to introduce optional filters, depending on the content of your XML. To continue on the example by StriplingWarrior:

var name = GetNameFromXml();
var city = GetCityFromXml();
var state = GetStateFromXml();

var result = someObj;
if (name != null)
    result = result.Where(i => i.Name == name);
if (city != null)
    result = result.Where(i => i.City == city);
if (state != null)
    result = result.Where(i => i.State == state);

This way, you would be applying any number of filters (from none to all three), depending on what is actually specified in your XML.

Upvotes: 5

Related Questions