Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reuse where clauses in Linq To Sql queries

I have users searching records of type Record. They type a search term in a textbox and then I search records by matching several fields with the search term.

My query looks like:

var results = from record in DataContext.Records
              where
                   record.Field1.ToLower().Contains(term) ||
                   record.Field2.ToLower().Contains(term) ||
                   record.Field3.ToLower().Contains(term)
              select record;

I have a number of queries that all use the same filter and thus I would like to extract the filtering so it can be reused. Something like:

var filter = new Func<Record, string, bool>(
                (record, term) =>
                    record.Field1.ToLower().Contains(term) ||
                    record.Field2.ToLower().Contains(term) ||
                    record.Field3.ToLower().Contains(term)
             );

var results = from record in DataContext.Records
              where filter(record, term)
              select record;

However, it does not work because:

Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL.

How can I reuse my where condition across queries?

like image 901
Xavier Poinas Avatar asked Feb 10 '11 01:02

Xavier Poinas


People also ask

Can we use multiple where clause in LINQ query?

A single query expression may have multiple where clauses.

How LINQ queries converted into SQL queries?

LINQ to SQL translates the queries you write into equivalent SQL queries and sends them to the server for processing. More specifically, your application uses the LINQ to SQL API to request query execution. The LINQ to SQL provider then transforms the query into SQL text and delegates execution to the ADO provider.

What is where clause in LINQ?

The where clause is used in a query expression to specify which elements from the data source will be returned in the query expression. It applies a Boolean condition (predicate) to each source element (referenced by the range variable) and returns those for which the specified condition is true.

Can we use LINQ to query against a database?

Language-Integrated Query (LINQ) makes it easy to access database information and execute queries. The following example shows how to create a new application that performs queries against a SQL Server database. The examples in this topic use the Northwind sample database.


2 Answers

You need to build an expression instead of a function:

Expression<Func<Record, bool>> filter = 
  record => record.Field1.ToLower().Contains(term);  // rest omitted

The lambda expression remains the same, but you need to return it into a variable of type Expression<Func<Record, bool>> -- that will make the C# compiler compile it as an expression instead of a delegate, allowing it to be passed to LINQ to SQL.

However, you won't be able to use an expression variable with a C#-syntax where clause: you'll need to use the Where extension method:

var results = DataContext.Records.Where(filter);

Edited to add: If you want to be able to create filters on different terms, you just need a method to produce an expression from a term:

private static Expression<Func<Record, bool>> Filter(string term)
{
  return r => r.Field1.ToLower().Contains(term);
}

var results = DataContext.Records.Where(Filter(term));

If you prefer to keep filter as a lambda as you have at the moment, you can do so, but the generics get a bit nested:

Func<string, Expression<Func<Record, bool>>> filter =
  term => (r => r.Field1.ToLower().Contains(term));

var results = DataContext.Records.Where(filter(term));

Regardless, the important thing is that what goes in the Where clause must be an Expression<Func<Record, bool>> -- but as shown above you can make the expression depend on term by building a suitable expression on the fly. Which is exactly what LINQ to SQL would be doing if you spelled out the filter longhand in the Where clause.

like image 173
itowlson Avatar answered Sep 21 '22 06:09

itowlson


Use a CompiledQuery!

var filter = CompiledQuery.Compile(
    (DatabaseDataContext dc, Record record, string term) =>
        record.Field1.ToLower().Contains(term) ||
        record.Field2.ToLower().Contains(term) ||
        record.Field3.ToLower().Contains(term)
);

var results = from record in DataContext.Records
              where filter(DataContext, record, term)
              select record;

For more information, see How to: Store and Reuse Queries.

like image 26
Jeff Mercado Avatar answered Sep 18 '22 06:09

Jeff Mercado