2012-03-12 18 views
10

Tengo un problema al construir una consulta de linq bastante fuerte. Básicamente, tengo una situación en la que necesito ejecutar una subconsulta en un bucle para filtrar el número de coincidencias que se devuelven desde la base de datos. código de ejemplo es en este bucle a continuación:múltiplo de Linq donde consultas

 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)); 
     } 

Cuando posteriormente que llamo el ToList() en la variable de consulta parece que sólo una sola de las subconsultas se ha realizado y me quedo con un cubo de datos Yo no requiero Sin embargo, este enfoque funciona:

 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)); 

Desafortunadamente, este enfoque es desagradable, ya que da lugar a varias consultas a la base de datos remota mediante el cual el planteamiento inicial si podía conseguir que funcione solamente resultaría en un solo golpe. ¿Algunas ideas?

Respuesta

8

Creo que está abordando un caso especial de captura de la variable de bucle en la expresión lambda utilizada para filtrar. También conocido como un acceso al error de cierre modificado.

Prueba esto:

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)); 
    } 

El problema es la captura de la variable parent en el cierre (que la sintaxis de LINQ se convierte a), lo que hace que todas las subQuery es que se ejecuten con el mismo ID de padre.

Lo que ocurre es que el compilador genera una clase para contener el delegado y las variables locales a las que accede el delegado. El compilador reutiliza la misma instancia de esa clase para cada ciclo; y por lo tanto, una vez que se ejecuta la consulta, todos los Where s se ejecutan con el mismo parent Guid, es decir, el último en ejecutarse.

Declarar el parent dentro del alcance del bucle hace que el compilador esencialmente haga una copia de la variable, con el valor correcto, para capturar.

Esto puede ser un poco difícil de entender al principio, por lo que si esta es la primera vez que lo golpea; Me gustaría recomendar estos dos artículos para el fondo y una explicación detallada:

+0

+1 Si quiere entender más este concepto, mire la respuesta de Skeet aquí y el artículo al que se hace referencia. http://stackoverflow.com/questions/271440/c-sharp-captured-variable-in-loop –

+0

@DMoses gracias, agregué algunos enlaces, reconociendo totalmente que no soy capaz de explicar esto tan elegante y preciso como los caballeros Lippert y Skeet :-) – driis

+0

Gracias driis. Trabajado como un encanto. Eres un santo y un erudito señor. – kh25

0

Tal vez de esta manera?

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

Gracias por la respuesta k06a, pero el La consulta anterior intenta (pero no tiene éxito) hacer algo ligeramente diferente a la consulta que presentó. Básicamente, estoy filtrando por todos los ParentAttributes mientras está filtrando por lo que esto devolverá un conjunto más grande de resultados. – kh25