2012-03-12 5 views
10

У меня проблема, создающая довольно внушительный запрос linq. В основном у меня есть ситуация, когда мне нужно выполнить подзапрос в цикле, чтобы отфильтровать количество совпадений, возвращаемых из базы данных. Пример кода в этом цикле ниже:Linq multiple where queries

 foreach (Guid parent in parentAttributes) 
     { 
      var subQuery = from sc in db.tSearchIndexes 
          join a in db.tAttributes on sc.AttributeGUID equals a.GUID 
          join pc in db.tPeopleIndexes on a.GUID equals pc.AttributeGUID 
          where a.RelatedGUID == parent && userId == pc.CPSGUID        
          select sc.CPSGUID; 

      query = query.Where(x => subQuery.Contains(x.Id)); 
     } 

Когда я впоследствии вызвать ToList() на переменной запроса представляется, что только один из подзапросов была выполнена, и я остался с ведром данных Я не требую. Однако этот подход работает:

 IList<Guid> temp = query.Select(x => x.Id).ToList(); 

     foreach (Guid parent in parentAttributes) 
     { 
      var subQuery = from sc in db.tSearchIndexes 
          join a in db.tAttributes on sc.AttributeGUID equals a.GUID 
          join pc in db.tPeopleIndexes on a.GUID equals pc.AttributeGUID 
          where a.RelatedGUID == parent && userId == pc.CPSGUID        
          select sc.CPSGUID; 

      temp = temp.Intersect(subQuery).ToList(); 
     } 

     query = query.Where(x => temp.Contains(x.Id)); 

К сожалению, такой подход противный, как это приводит к множеству запросов к удаленной базе данных, при этом первоначальный подход, если я мог бы получить его работу приведет лишь один удар. Есть идеи?

ответ

8

Я думаю, что вы делаете особый случай захвата переменной цикла в выражении лямбда, используемом для фильтрации. Также известен как доступ к модифицированной ошибке закрытия.

Попробуйте это:

foreach (Guid parentLoop in parentAttributes) 
    { 
     var parent = parentLoop; 
     var subQuery = from sc in db.tSearchIndexes 
         join a in db.tAttributes on sc.AttributeGUID equals a.GUID 
         join pc in db.tPeopleIndexes on a.GUID equals pc.AttributeGUID 
         where a.RelatedGUID == parent && userId == pc.CPSGUID        
         select sc.CPSGUID; 

     query = query.Where(x => subQuery.Contains(x.Id)); 
    } 

Проблемы захват parent переменных в замыкании (что синтаксис LINQ преобразуются в), что вызывает все subQuery эсов будут работать с тем же родительским идентификатором.

Что происходит, когда компилятор генерирует класс для хранения делегата и локальных переменных, к которым обращается делегат. Компилятор повторно использует один и тот же экземпляр этого класса для каждого цикла; и поэтому, как только запрос выполняется, все Where s исполняется с тем же parent Guid, а именно последним для выполнения.

Объявление parent внутри области действия цикла заставляет компилятор по существу сделать копию переменной с правильным значением, которое должно быть записано.

Это может быть немного сложно понять сначала, поэтому, если это первый раз, когда он ударил вас; Я рекомендовал бы эти две статьи для фона и подробное объяснение:

+0

+1 Если вы хотите больше понять эту концепцию, посмотрите на ответ Скита здесь и его статью, на которую ссылается. http://stackoverflow.com/questions/271440/c-sharp-captured-variable-in-loop –

+0

@DMoses спасибо, я добавил несколько ссылок, полностью признав, что я не могу объяснить это как изящный и точный, как джентльмены Lippert и Skeet :-) – driis

+0

Спасибо, driis. Работал как шарм. Вы святой и ученый, сэр. – kh25

0

Может быть так?

var subQuery = from sc in db.tSearchIndexes 
       join a in db.tAttributes on sc.AttributeGUID equals a.GUID 
       join pc in db.tPeopleIndexes on a.GUID equals pc.AttributeGUID 
       where parentAttributes.Contains(a.RelatedGUID) && userId == pc.CPSGUID        
       select sc.CPSGUID; 
+0

Спасибо за ответ k06a, но запрос выше пытается (но не преуспевает) в выполнении чего-то немного другого, чем запрос, который вы представили. По сути, я фильтрую все атрибуты parentAttributes, пока вы фильтруете их, поэтому это приведет к большему набору результатов. – kh25