2010-05-13 11 views
5

Cuando ejecuto este fragmento de código:¿Por qué mis parámetros vinculados son todos idénticos (usando Linq)?

string[] words = new string[] { "foo", "bar" }; 
var results = from row in Assets select row; 
foreach (string word in words) 
{ 
    results = results.Where(row => row.Name.Contains(word)); 
} 

me sale este SQL:

-- Region Parameters 
DECLARE @p0 VarChar(5) = '%bar%' 
DECLARE @p1 VarChar(5) = '%bar%' 
-- EndRegion 
SELECT ... FROM [Assets] AS [t0] 
WHERE ([t0].[Name] LIKE @p0) AND ([t0].[Name] LIKE @p1) 

Tenga en cuenta que @p0@p1 y son a la vez bar, cuando yo quería que fueran foo y bar.

Supongo que Linq de alguna manera vincula una referencia a la variable word en lugar de una referencia a la cadena a la que hace referencia actualmente word? ¿Cuál es la mejor manera de evitar este problema?

(También, si tiene alguna sugerencia para un mejor título para esta pregunta, por favor ponga en los comentarios.)

Tenga en cuenta que he intentado esto con LINQ regular también, con los mismos resultados (se puede pegar este derecho en LINQPad):

string[] words = new string[] { "f", "a" }; 
string[] dictionary = new string[] { "foo", "bar", "jack", "splat" }; 
var results = from row in dictionary select row; 
foreach (string word in words) 
{ 
    results = results.Where(row => row.Contains(word)); 
} 
results.Dump(); 

volcados:

bar 
jack 
splat 
+1

Para comentarios adicionales y el análisis de este tema ver http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered- harmful.aspx y http://blogs.msdn.com/ericlippert/archive/2009/11/16/closing-over-the-loop-variable-part-two.aspx –

Respuesta

8

usted está utilizando lo que se llama un "cierre", lo que significa que se está definiendo una función anónima (su lambda) que usa una variable local en su cuerpo. Específicamente, está "cerrando" sobre la variable de bucle word. El problema que surge con los cierres resulta de ejecución diferida, lo que significa que el cuerpo de su lambda no se ejecuta cuando lo define, sino cuando se invoca.

Debido a esto, casi nunca desea cerrar la variable de bucle. Debido a la ejecución retrasada, el valor de la variable dentro de la lambda será lo que sea cuando se ejecute la lambda. Para lambdas declarado dentro de un bucle e invocado fuera de él, esto significa que siempre tendrá el último valor del bucle.

Para contrarrestar esto, use una variable local declarada dentro del ciclo. Esto hará que capture el valor en ese punto en el tiempo y pase una nueva variable a cada lambda que se crea. De este modo:

string[] words = new string[] { "foo", "bar" }; 
var results = from row in Assets select row; 
foreach (string word in words) 
{ 
    string tempWord = word; 

    results = results.Where(row => row.Name.Contains(tempWord)); 
} 
+0

Gracias, excelente descripción e incluso funciona! :) –

Cuestiones relacionadas