2009-12-21 4 views
2

Мне нужно написать запрос, который будет выполнять поиск по ключевым словам в таблице базы данных. Код в настоящее время что-то вроде этого (хотя и с жестко заданным набором ключевых слов) выглядит:Запрос с несколькими типами

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 бросает «Доступ к модифицированному закрытия» предупреждение для ключевого слова внутри Еогеасп. Я понимаю ошибку, и подтвердить эту проблему, когда я смотрю на сгенерированный 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 

Поскольку изменения итераторных ключевое слово во время цикла, мой SQL содержит только ссылку на последнее значение («Чарли») ,

Что мне делать, чтобы избежать этой проблемы? Возможно, я мог бы преобразовать рассказы в список, прежде чем применять каждое новое ключевое слово where, но это кажется неэффективным.

решаемые

Спасибо за все ответы. В конечном счете у меня возникли две отдельные проблемы: оба из которых были разрешены:

  1. Используйте локальную переменную внутри цикла foreach(), чтобы избежать проблемы с доступом «Доступ к модифицированному закрытию».
  2. Используйте PredicateBuilder в LINQKit, чтобы динамически собирать список предложений OR, чтобы разрешить поиск «любого» стиля.

ответ

5

Назначьте переменную временным в рамках блока foreach, чтобы каждый раз получать новую переменную.

foreach(var keyword in keywords) 
{ 
    var kwd = keyword; 
    stories = from story in stories where story.Name.Contains (kwd)); 
} 

Эрик Липперт имеет good article (или два), объясняющие опасность включая переменную цикла в закрытии и как избежать его.

+0

+1 для быстрых пальцев;) Для получения дополнительной информации, ознакомьтесь с http://blogs.msdn.com/ericlippert/archive/2009/11/12/ close-over-the-loop-variable-thought-harm.aspx –

+0

Я только что добавил эту ссылку к моему сообщению ... lol. – tvanfosson

1

Вот очень простой способ:

var keywords = new [] { "alpha", "bravo", "charlie" }; 
IQueryable<Story> stories = DataContext.Stories; 

foreach(var keyword in keywords) 
{ 
    string kw = keyword; 
    stories = from story in stories where story.Name.Contains (kw)); 
} 

return stories; 

Вы также мог бы рассмотреть

var keywords = new [] { "alpha", "bravo", "charlie" }; 
IQueryable<Story> stories = DataContext.Stories 
             .Where(story => keywords.All(kw => story.Name.Contains(kw)); 
1

Вам нужно сделать локальную копию ключевого слова:

foreach(var keyword in keywords) 
{ 
    var localKeyword = keyword; 
    stories = from story in stories where story.Name.Contains (localKeyword)); 
} 
+0

Почему downvote? –

0

Я просто решить часть моя проблема. «Доступ к модифицированному закрытию» легко крепятся с помощью локально-контекстной копии переменного ключевого слова, например:

var keywords = new [] { "alpha", "bravo", "charlie" }; 
IQueryable<Story> stories = DataContext.Stories; 

foreach(var keyword in keywords) 
{ 
    var innerKeyword = keyword; 
    stories = from story in stories where story.Name.Contains (innerKeyword)); 
} 

return stories; 

К сожалению, добавление несколько, где пункты могут не работать для меня. Каждый LINQ, где выражение разделяется символами AND, возвращаются только те истории, которые удовлетворяют всем ключевым словам. Мне нужны истории с любыми ключевыми словами.

+0

Вопрос AND/OR был задан раньше, найдите статьи о Dynamic SQL здесь или в блоге ScottGu ... – Codewerks

0

Не пробовал, но что-то вроде этой работы?

from story in stories 
where keywords.All(kw => story.Name.Contains(kw)); 
0

Редактировать: Комментарий неверно.

Если вы хотите найти истории, которые соответствуют одному или нескольким ключевым словам, а не всем ключевым словам, как вы укажете в своем последующем ответе, вы должны использовать оператор Any. Я считаю, что следующий запрос (непроверенный) будет работать:

IQueryable<Story> matchingStories = 
    from story in stories 
    where keywords.Any(keyword => story.Name.Contains(keyword)); 
+0

Любое не работает. Я получаю исключение NotSupportedException: «Локальная последовательность не может использоваться в реализации операторов запросов LINQ to SQL, кроме оператора Contains()». – MikeWyatt

+0

Ах, извините, я должен подумать, прежде чем набрать ... Вам нужно изучить LinqKit и его PredicateBuilder. Посмотрите эту страницу, второй фрагмент кода в значительной степени вы хотите: http://www.albahari.com/nutshell/predicatebuilder.aspx –

+0

Я попытался преобразовать свой список строк в запрос через AsQueryable() и что вызвало исключение переполнения стека, когда я выполнил запрос. – MikeWyatt

Смежные вопросы