views:

9021

answers:

2

Hi,

I have a course table which I need to search based on keywords typed in the search box. Here is a sample query:

SELECT * FROM Courses WHERE 
Title LIKE '%word%' OR Title LIKE '%excel%' OR 
Contents LIKE '%word%' OR Contents LIKE '%excel%'

How can I convert this in LINQ where LINQ would dynamically generate WHERE statements based on each keywords.

I tried to user PredicateBuilder it works fine as long as the field is VARCHAR. For the "TEXT" fields the quotes are not generated thus causing compiler to give an error message. Here is the SQL generated by PredicateBuilder

SELECT [t0].[CoursesID], [t0].[Title], [t0].[Contents], [t0].[Active], 
FROM [dbo].[Courses] AS [t0]
WHERE ([t0].[Title] LIKE '%word%') OR ([t0].[Contents] LIKE %word%) OR 
([t0].Title] LIKE '%excel%') OR ([t0].[Contents] LIKE %excel%)

Notice there is no single Quote for the "Contents" field which is a Text field in the database.

Is there any easy way to build WHERE statement and attach it with query? Does anyone know how I can do this without PredicateBuilder?

Thanks in advance.

A: 

As predicate builder doesn't know the DB type of the property the Contains method is called on, I guess this might be a problem inside linq to sql. Have you tried with a normal query (not with predicate builder) and a TEXT column with Contains?

Frans Bouma
+9  A: 

Since you are working w/ LINQ I suppose you are working against a LINQ-to-SQL data context right? I don't have a spare DataContext lying around to test this, but this should give you some ideas.

I don't know if it will work against data context though, but most of these are pretty basic stuff (chaining OR operator and Contains method call) so it shouldn't cause problem when the query translates to SQL.

First I create a custom function that would build my predicate:

Func<string, Func<DataItem, bool>> buildKeywordPredicate =
    keyword =>
        x => x.Title.Contains(keyword)
            || x.Contents.Contains(keyword);

This is a function which takes a single string keyword and then return another function which takes a DataItem and checks it against the keyword.

Basically, if you pass in "Stack", you'll get a predicate: x => x.Title.Contains("Stack") || x.Contents.Contains("Stack").

Next, since there are many possible keywords and you need to chain it with an OR operation, I create another helper function to chain 2 predicates together with an OR

Func<Func<DataItem,bool>, Func<DataItem, bool>, Func<DataItem, bool>> buildOrPredicate =
    (pred1, pred2) =>
        x => pred1(x) || pred2(x);

This function takes 2 predicates and then join them up with an OR operation.

Having those 2 functions, I can then build my where predicate like this:

foreach (var word in keywords) {            
    filter = filter == null
        ? buildKeywordPredicate(word)
        : buildOrPredicate(filter, buildKeywordPredicate(word));
}

The first line inside the loop basically checks if the filter is null. If it is, then we want a simple keyword filter built for us.

Else if the filter is not null, we need to chain existing filters with an OR operation, so we pass the existing filter and a new keyword filter to buildOrPredicate to do just that.

And then we can now create the WHERE part of the query:

var result = data.Where(filter);

Passing in the complicated predicate we've just built.

I don't know if this will different from using PredicateBuilder but since we are deferring query translation to the LINQ-to-SQL engine, there should not be any problems.

But as I said, I havn't tested it against a real data context, so if there's any problems you can write in the comments.

Here's the console app that I built to test: http://pastebin.com/feb8cc1e

Hope this helps!


EDIT: For a more generic and reusable version which involves properly utilizing the Expression Trees in LINQ, check out Thomas Petricek's blog post: http://tomasp.net/articles/dynamic-linq-queries.aspx

chakrit
This will unfortunatelly work only for functions. To make this work with Expression Trees you need to use a trick like this: http://tomasp.net/articles/dynamic-linq-queries.aspx
Tomas Petricek
That is some feat you did there!.. The same trick but more generic and more awesomeness... Anyway, I'm a subscriber of your blog now :-)
chakrit
Thanks, this one worked. http://tomasp.net/articles/dynamic-linq-queries.aspx – Tomas Petricek
Amir
I'm looking for an easy solutions. Something like: var q = q.Where(<t-sql statement>)How can you write a custom Where statement and attach it with LINQ?
Amir