I need to write a query that will perform a keyword search on a database table. The code currently looks something like this (albeit with a hard-coded set of keywords):
var keywords = new [] { "alpha", "bravo", "charlie" };
IQueryable<Story> stories = DataContext.Stories;
foreach( var keyword in keywords )
{
stories = from story in stories where story.Name.Contains ( keyword ) );
}
return stories;
ReSharper throws a "Access to modified closure" warning for keyword inside the foreach. I understand the error, and confirm the problem when I look at the generated SQL:
SELECT [t0].[Id], [t0].[Name]
FROM [dbo].[Story] AS [t0]
WHERE (([t0].[Name] LIKE @p0))
AND (([t0].[Name] LIKE @p1))
AND (([t0].[Name] LIKE @p2))
-- @p0: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [%charlie%]
-- @p1: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [%charlie%]
-- @p2: Input NVarChar (Size = 9; Prec = 0; Scale = 0) [%charlie%]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1
Because the keyword iterator changes during the loop, my SQL only contains a reference to the last value ("charlie").
What should I do to avoid this problem? I could probably convert the stories queryable to a list before applying each new keyword where clause, but that seems inefficient.
SOLVED
Thanks for all the answers. Ultimately I had two separate problems, both of which have been resolved:
- Use local variable inside foreach() loop to avoid "Access to modified closure" problem.
- Use PredicateBuilder in LINQKit to dynamically assemble a list of OR clauses to allow for "any"-style keyword searches.