Запрос с несколькими адресами - PullRequest
2 голосов
/ 21 декабря 2009

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

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 выдает предупреждение "Доступ к измененному закрытию" для ключевого слова внутри foreach. Я понимаю ошибку и подтверждаю проблему, когда смотрю на сгенерированный 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 содержит только ссылку на последнее значение ("Чарли").

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

решаемые

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

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

Ответы [ 6 ]

5 голосов
/ 21 декабря 2009

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

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

У Эрика Липперта есть хорошая статья (или две), объясняющая опасность включения переменной цикла в замыкание и как ее избежать.

1 голос
/ 21 декабря 2009

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

foreach( var keyword in keywords )
{
    var localKeyword = keyword;
    stories = from story in stories where story.Name.Contains ( localKeyword ) );
}
1 голос
/ 21 декабря 2009

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

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));
0 голосов
/ 21 декабря 2009

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

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

IQueryable<Story> matchingStories = 
    from story in stories
    where keywords.Any(keyword => story.Name.Contains(keyword));
0 голосов
/ 21 декабря 2009

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

from story in stories
where keywords.All(kw => story.Name.Contains(kw));
0 голосов
/ 21 декабря 2009

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

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;

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

...