45

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?

P.Brian.Mackey
  • 43,228
  • 68
  • 238
  • 348
  • 2
    There are a couple of different ways to do this. Will it always be Property == Value. Are less than, greater than, not equal possible? Can the XML specify more than one value? `Name = 'Bob' || Name = 'Fred'`. Are conditions only combined with &&? `Name = 'Bob' && State = 'OH'`. Or can the conditions be combined with ||? `Name = 'Bob' || State = 'OH'`. All of these need to be taken into account before a solution can be proposed. – cadrell0 Feb 29 '12 at 19:19
  • 1
    The question lacks clarity; it doesn’t impart that the XML structure will contain conditional expressions (rather than just desired values to be matched, as `Name = 'Bob'` seems to imply). It’s stupid and unhelpful that someone is finding it fit to go round and downvote all answers that did not infer this missing information. – Douglas Feb 29 '12 at 19:38
  • @Cadrell0 - More complex queries would be a very nice addition. I tried to start the question off simple with just a where clause. I didn't know if that would be a very complex solution in and of itself. Judging by the answers, there seems to be a whole library available to achieve the tasks. Dynamic LINQ by ScottGU looks promising. – P.Brian.Mackey Feb 29 '12 at 19:38
  • you should try the Gridify library: https://github.com/alirezanet/Gridify – AliReza Sabouri Sep 18 '21 at 18:46

7 Answers7

150

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.

Balazs Tihanyi
  • 6,659
  • 5
  • 23
  • 24
  • 7
    +1 because while expression trees can be a bit tough to understand at first, but they are a good solution for this problem. Once you get past the initial examples that are out there just watch out for handling null values and ensuring the types used in your expressions match each other. Depending on how "dynamic" the source of your queries are (e.g., all Expression.Constants() come in as strings even though they're compared to an int) you may need to do some additional conversions. This MSDN article is a good ref too - http://msdn.microsoft.com/en-us/library/bb882637.aspx – Sam Storie Aug 23 '14 at 15:15
  • 5
    I love you man. you simplified this mess in two lines of code. Let me point that `someObj` has to be IQueryable to accept `Where(exp)` – Ivan Ferrer Villa Jan 13 '16 at 20:54
  • This solution also would be nice if you would like to using dynamic Column Name ( in this case "NAME" ) in equal Linq query. – Hamid Feb 15 '16 at 16:34
  • realy helpfull but it's faster to use LinqKit library https://github.com/scottksmith95/LINQKit – VinnyG Sep 21 '16 at 18:01
  • Really wish we could build Query syntax (not Lambda syntax) this way... – General Grievance Aug 16 '21 at 20:19
34

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#.

Steve Wortham
  • 21,740
  • 5
  • 68
  • 90
  • 4
    Your Idea gave me a lead in solving my issue without going for complicated dynamic Linq libraries. Solved it the native way. Thanks – Ananda Jul 12 '16 at 10:23
  • This worked well for me. I forgot that we used to do this in SQL Stored Procs so it's easy to see why it would work in Linq. – Caverman Apr 17 '17 at 20:38
30

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.

Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
  • `Where("SomeText")` Yup. You nailed the concept I'm looking to achieve. – P.Brian.Mackey Feb 29 '12 at 19:42
  • 8
    This is an older post so this may not be relevant to this specific question, but Dynamic Linq is not a built-in feature of the framework. It was released as source code that you can download and compiled into your application (https://github.com/kahanu/System.Linq.Dynamic). Depending on your application this may not be suitable, so just wanted to raise some awareness about this. – Sam Storie Aug 23 '14 at 15:22
9

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);
aledpardo
  • 761
  • 9
  • 19
Antineutrino
  • 1,093
  • 3
  • 10
  • 26
9

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 :)

Justin Pihony
  • 66,056
  • 18
  • 147
  • 180
  • Antineutrino's answer is probably the easiest, where you can pass in strings. However, I believe that the .compile method in Expression Tree's gives you type checking, so it is a little safer in that sense. However, implementing expression trees is not trivial by what I have heard. – Justin Pihony Feb 29 '12 at 19:24
  • 4
    Why was this downvoted? It definitely seems to fit the problem, so I would appreciate a reason for the downvote? – Justin Pihony Feb 29 '12 at 19:29
  • 1
    Expression trees - fun for the whole family :) Up-voted because none of the other solution even mentions them. – Vladislav Zorov Feb 29 '12 at 19:42
5

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.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • no, you missed his question. how do you create linq queries from a text field? – jcolebrand Feb 29 '12 at 19:17
  • @jcolebrand: Can you clarify what you mean by "create linq queries from a text field?" As far as I can tell, I've answered the question as well as can be hoped, considering the vagueness of the question. – StriplingWarrior Feb 29 '12 at 19:28
5

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.

Douglas
  • 53,759
  • 13
  • 140
  • 188