Linq a SQL cómo hacer "dónde [columna] en (lista de valores)"

101

Tengo una función en la que obtengo una lista de identificadores y necesito devolver una lista que coincida con una descripción asociada con el identificador. P.ej:

public class CodeData
{
    string CodeId {get; set;}
    string Description {get; set;}
}

public List<CodeData> GetCodeDescriptionList(List<string> codeIDs)
    //Given the list of institution codes, return a list of CodeData
    //having the given CodeIds
}

Entonces, si estuviera creando el sql para esto yo mismo, simplemente haría algo como lo siguiente (donde la cláusula in contiene todos los valores en el argumento codeIds):

Select CodeId, Description FROM CodeTable WHERE CodeId IN ('1a','2b','3')

En Linq to Sql, parece que no puedo encontrar el equivalente de la cláusula "IN". Lo mejor que he encontrado hasta ahora (que no funciona) es:

 var foo = from codeData in channel.AsQueryable<CodeData>()
           where codeData.CodeId == "1" || codeData.CodeId == "2"
           select codeData;

El problema es que no puedo generar dinámicamente una lista de cláusulas "O" para linq a sql, porque están configuradas en tiempo de compilación.

¿Cómo se logra una cláusula where que comprueba que una columna está en una lista dinámica de valores usando Linq to Sql?

Nathan
fuente

Respuestas:

159

Utilizar

where list.Contains(item.Property)

O en tu caso:

var foo = from codeData in channel.AsQueryable<CodeData>()
          where codeIDs.Contains(codeData.CodeId)
          select codeData;

Pero también podría hacerlo en notación de puntos:

var foo = channel.AsQueryable<CodeData>()
                 .Where(codeData => codeIDs.Contains(codeData.CodeId));
Jon Skeet
fuente
¿Cómo se usa en caso de CodeId es Integer?
Kiran Solkar
2
@KiranSolkar: Entonces presumiblemente codeIDssería un List<int>, y todo estaría bien.
Jon Skeet
@JonSkeet ¿No se distingue entre mayúsculas y minúsculas? Si codeIDs es una lista de cadenas en mayúsculas y codeData.codeId es una cadena en minúsculas, fallará.
PersyJack
@PersyJack: No había nada en la pregunta acerca de que tuviera que distinguir entre mayúsculas y minúsculas. En cuanto a si lo sería o no, no puedo recordar si LINQ to SQL aplica la distinción entre mayúsculas y minúsculas de forma predeterminada o deja que la configuración de la base de datos lo gobierne.
Jon Skeet
1
@PersyJack LINQ to SQL genera la consulta T-SQL, que luego se ejecuta en SQL Server utilizando la configuración de la base de datos para distinguir entre mayúsculas y minúsculas. Aunque, si uno no tiene cuidado y materializa los resultados de la consulta, antes de aplicar LINQ a los objetos en memoria, pueden sufrir las consecuencias de una distinción entre mayúsculas y minúsculas no coincidente.
Zarepheth
26

También puedes usar:

List<int> codes = new List<int>();

codes.add(1);
codes.add(2);

var foo = from codeData in channel.AsQueryable<CodeData>()
          where codes.Any(code => codeData.CodeID.Equals(code))
          select codeData;
Nick DeMayo
fuente
1
Tuve que usar esto ya que nuestra implementación de IQToolkit no es compatible con .Contains ()
DJ van Wyk
1

Había estado usando el método en la respuesta de Jon Skeet, pero se me ocurrió usar otro Concat. El Concatmétodo funcionó un poco mejor en una prueba limitada, pero es una molestia y probablemente me quedaré Contains, o tal vez escribiré un método auxiliar para hacer esto por mí. De cualquier manera, aquí hay otra opción si alguien está interesado:

El método

// Given an array of id's
var ids = new Guid[] { ... };

// and a DataContext
var dc = new MyDataContext();

// start the queryable
var query = (
    from thing in dc.Things
    where thing.Id == ids[ 0 ]
    select thing 
);

// then, for each other id
for( var i = 1; i < ids.Count(); i++ ) {
    // select that thing and concat to queryable
    query.Concat(
        from thing in dc.Things
        where thing.Id == ids[ i ]
        select thing
    );
}

Prueba de rendimiento

Esto no era ni remotamente científico. Imagino que la estructura de su base de datos y la cantidad de ID involucrados en la lista tendrían un impacto significativo.

Configuré una prueba en la que hice 100 pruebas cada una Concaty Containsen la que cada prueba implicó seleccionar 25 filas especificadas por una lista aleatoria de claves primarias. He ejecutado esto alrededor de una docena de veces, y la mayoría de las veces el Concatmétodo sale entre un 5 y un 10% más rápido, aunque una vez el Containsmétodo ganó por una pizca.

DCShannon
fuente
0
 var filterTransNos = (from so in db.SalesOrderDetails
                    where  ItemDescription.Contains(ItemDescription)
                            select new { so.TransNo }).AsEnumerable();    


listreceipt = listreceipt.Where(p => filterTransNos.Any(p2 => p2.TransNo == p.TransNo)).ToList();
Deepan Raj
fuente
-1

Así es como lo hago usando HashSet

        HashSet<String> hs = new HashSet<string>(new String[] { "Pluto", "Earth", "Neptune" });
        String[] arr =
        {
            "Pluto",
            "Earth",
            "Neptune",
            "Jupiter",
            "Saturn",
            "Mercury",
            "Pluto",
            "Earth",
            "Neptune",
            "Jupiter",
            "Saturn",
            "Mercury",
            // etc.
        };
        ICollection<String> coll = arr;

        String[] arrStrFiltered = coll.Where(str => hs.Contains(str)).ToArray();

HashSet es básicamente casi O (1) por lo que su complejidad sigue siendo O (n).

MG
fuente
Se trata de LINQ-to-SQL. Tales consideraciones de LINQ-to-objects no se aplican.
Gert Arnold
ICollection también puede provenir de un LINQ-SQL, esta es una forma genérica
MG
La pregunta es cómo construir una expresión que se traduzca en SQL correcto. Eso no tiene nada que ver con cómo buscar una colección local. Su respuesta solo engañará a los futuros lectores que no sean conscientes de esta distinción.
Gert Arnold