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?
A single query expression may have multiple where clauses.
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With