Leonardo
Leonardo

Reputation: 11389

Complex Dynamic LINQ Query

I`m using Linq To Entities to power up my app and i want to create quite a complex search query... see what I want is:

Upon a user entering a string like "Washington Boston" i will break it in N strings, according to the amount of " "(empty spaces, in this case 2 strings) and search my Counties table for Washington and for Boston

If this were a "static" query i would write it like this:where county.Name.Contains("Washington") || county.Name.Contains("Boston") but i don't know how many cities (or white spaces for this matter) the user will enter...

In a classic pure TSQL enviroment i would compile the query on runtime and use the command Exec... so my question here is: How to dynamically generate a LINQ to entities query?

Please take note that .Where() is not a extension method based on delegates... it does not translate to TSQL on the backend, meaning from e in context.Counties.Where(c => c.Name.Contains("boston")) translates to SELECT ID,NAME FROM COUNTIES on the backend

Upvotes: 0

Views: 883

Answers (3)

Kirk Broadhurst
Kirk Broadhurst

Reputation: 28698

I'll just expand David Khaykin's comment into an answer, because it is the simplest way to achieve the requirement.

You simply need to split the search term into a list of terms, and then filter your counties entity using the Contains method according to your requirement

var terms = searchTerm.Split(' ').ToList();

// an exact match  
counties.Where(c => terms.Contains(c.Name))

// a 'contains' or 'like' type match
counties.Where(c => terms.Any(t => c.Contains(t));

The Contains method is the System.Linq.Enumerable.Contains, not String.Contains.

When writing any complex dynamic LINQ queries, you can use the method outlined by bmused - very powerful. But for this type of simple filtering you can pass straight lists.

Upvotes: 4

meataxe
meataxe

Reputation: 981

You could use Predicate builder (and LINQKit because you're using Entity Framework), which will let you do this:

IQueryable<County> SearchInCountyNames(params string[] countyNames)
{
  var predicate = PredicateBuilder.False<County>();

  foreach (string countyName in countyNames)
  {
    string name = countyName;
    predicate = predicate.Or(c => c.Name.Contains(name));
  }


  return dataContext.Counties.AsExpandable().Where(predicate);
}

There's a bunch more examples on the predicate builder site.

Upvotes: 2

user1914530
user1914530

Reputation:

you could use System.Linq.Expressions

So you would do something like (Not tested):

public Expression<Func<County, Boolean>> GetPredicate(List<String> terms)
{
//The string method - Contains
MethodInfo mContains = typeof(String).GetMethod("Contains");

//The parameter - county
ParameterExpression pe = Expression.Parameter(typeof(County), "County");

//The property - Name
Expression property = Expression.Property(pe, "Name");

//expression body
Expression body = null;

foreach(String term in terms)
{
  //the constant to look for
  Expression searchTerm = Expression.Constant(term);

   //build expression body
   if(body == null) body = Expression.Call(property, mContains, searchTerm);
   else body = Expression.OrElse(body, 
       Expression.Call(property, mContains, searchTerm));
}

//create predicate
return Expression.Lambda<Func<County, Boolean>>(body, pe);
}

Upvotes: 2

Related Questions