Linq to SQL come fare "dove [colonna] in (elenco di valori)"


101

Ho una funzione in cui ottengo un elenco di ID e devo restituire un elenco corrispondente a una descrizione associata all'ID. Per esempio:

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
}

Quindi, se stessi creando lo sql per questo, farei semplicemente qualcosa di simile al seguente (dove la clausola in contiene tutti i valori nell'argomento codeIds):

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

In Linq a Sql non riesco a trovare l'equivalente della clausola "IN". Il migliore che ho trovato finora (che non funziona) è:

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

Il problema è che non posso generare dinamicamente un elenco di clausole "OR" da linq a sql, perché sono impostate in fase di compilazione.

Come si realizza una clausola where che controlla che una colonna sia in un elenco dinamico di valori utilizzando Linq to Sql?

Risposte:


159

Uso

where list.Contains(item.Property)

O nel tuo caso:

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

Ma potresti anche farlo in notazione punto:

var foo = channel.AsQueryable<CodeData>()
                 .Where(codeData => codeIDs.Contains(codeData.CodeId));

come usare in caso di CodeId è Integer ??
Kiran Solkar

2
@KiranSolkar: Allora presumibilmente codeIDssarebbe un List<int>, e tutto andrebbe bene.
Jon Skeet

@ JonSkeet Non fa distinzione tra maiuscole e minuscole? Se codeIDs è un elenco di stringhe maiuscole e codeData.codeId è una stringa minuscola, fallirà.
PersyJack

@ PersyJack: Non c'era nulla nella domanda sul fatto che non facesse distinzione tra maiuscole e minuscole. Per quanto riguarda il fatto che lo sia o meno, non ricordo se LINQ to SQL applica la distinzione tra maiuscole e minuscole per impostazione predefinita o lascia che le impostazioni del database lo governino.
Jon Skeet

1
@PersyJack LINQ to SQL genera la query T-SQL, che viene quindi eseguita su SQL Server utilizzando le impostazioni del database per la distinzione tra maiuscole e minuscole. Sebbene, se non si presta attenzione e si materializzano i risultati della query, prima di applicare LINQ agli oggetti in memoria, potrebbero subire le conseguenze della distinzione tra maiuscole e minuscole non corrispondenti.
Zarepheth,

26

Potresti anche usare:

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;

1
Ho dovuto usarlo poiché la nostra implementazione di IQToolkit non supporta .Contains ()
DJ van Wyk

1

Avevo usato il metodo nella risposta di Jon Skeet, ma un altro mi è venuto in mente usando Concat. Il Concatmetodo ha funzionato leggermente meglio in un test limitato, ma è una seccatura e probabilmente continuerò a seguirlo Contains, o forse scriverò un metodo di supporto per farlo per me. Ad ogni modo, ecco un'altra opzione se qualcuno è interessato:

Il metodo

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

Test della prestazione

Questo non era neanche lontanamente scientifico. Immagino che la struttura del database e il numero di ID coinvolti nell'elenco avrebbero un impatto significativo.

Ho impostato un test in cui ho eseguito 100 prove ciascuna Concate in Containscui ciascuna prova prevedeva la selezione di 25 righe specificate da un elenco casuale di chiavi primarie. L'ho eseguito circa una dozzina di volte e la maggior parte delle volte il Concatmetodo risulta più veloce del 5-10%, anche se una volta il Containsmetodo ha vinto solo per un po '.


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

-1

Ecco come lo faccio 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 è fondamentalmente quasi O (1) quindi la tua complessità rimane O (n).


Si tratta di LINQ-to-SQL. Tali considerazioni LINQ-to-objects non si applicano.
Gert Arnold

ICollection può provenire anche da LINQ-SQL, questo è un modo generico
MG

La domanda è su come costruire un'espressione che si traduca in SQL corretto. Non ha nulla a che fare con la ricerca in una raccolta locale. La tua risposta illuderà solo i futuri lettori che non sono consapevoli di questa distinzione.
Gert Arnold,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.