Linq to SQL, wie man "wo [Spalte] in (Liste der Werte)" macht

101

Ich habe eine Funktion, mit der ich eine Liste von IDs erhalte, und ich muss eine Liste zurückgeben, die einer Beschreibung entspricht, die der ID zugeordnet ist. Z.B:

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
}

Wenn ich also die SQL dafür selbst erstellen würde, würde ich einfach Folgendes tun (wobei die in-Klausel alle Werte im codeIds-Argument enthält):

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

In Linq to Sql kann ich anscheinend nicht das Äquivalent der "IN" -Klausel finden. Das Beste, was ich bisher gefunden habe (was nicht funktioniert) ist:

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

Das Problem ist, dass ich keine Liste von "ODER" -Klauseln für linq to sql dynamisch generieren kann, da diese zur Kompilierungszeit festgelegt werden.

Wie erreicht man eine where-Klausel, die eine Spalte in einer dynamischen Werteliste mit Linq to Sql überprüft?

Nathan
quelle

Antworten:

159

Verwenden

where list.Contains(item.Property)

Oder in Ihrem Fall:

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

Aber Sie können das genauso gut in Punktnotation tun:

var foo = channel.AsQueryable<CodeData>()
                 .Where(codeData => codeIDs.Contains(codeData.CodeId));
Jon Skeet
quelle
Wie verwende ich im Fall von CodeId ist Integer?
Kiran Solkar
2
@KiranSolkar: Dann codeIDswäre vermutlich ein List<int>, und alles wäre in Ordnung.
Jon Skeet
@ JonSkeet Ist das nicht Groß- und Kleinschreibung? Wenn codeIDs eine Liste von Zeichenfolgen in Großbuchstaben ist und codeData.codeId eine Zeichenfolge in Kleinbuchstaben ist, schlägt dies fehl.
PersyJack
@PersyJack: Es gab nichts in der Frage, ob die Groß- und Kleinschreibung nicht berücksichtigt werden muss. Ich kann mich nicht erinnern, ob LINQ to SQL standardmäßig die Groß- und Kleinschreibung erzwingt oder ob die Datenbankeinstellungen dies regeln.
Jon Skeet
1
@PersyJack LINQ to SQL generiert die T-SQL-Abfrage, die dann auf dem SQL Server unter Verwendung der Datenbankeinstellungen ausgeführt wird, um die Groß- und Kleinschreibung zu berücksichtigen. Wenn man jedoch nicht vorsichtig ist und die Abfrageergebnisse materialisiert, bevor LINQ auf speicherinterne Objekte angewendet wird, können sie die Konsequenzen einer nicht übereinstimmenden Groß- und Kleinschreibung haben.
Zarepheth
26

Sie könnten auch verwenden:

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
quelle
1
Ich musste dies verwenden, da unsere Implementierung von IQToolkit .Contains ()
DJ van Wyk am
1

Ich hatte die Methode in Jon Skeets Antwort verwendet, aber mir fiel eine andere ein Concat. Die ConcatMethode schnitt in einem begrenzten Test etwas besser ab, aber es ist ein Ärger und ich werde wahrscheinlich einfach dabei bleiben Contains, oder vielleicht schreibe ich eine Hilfsmethode, um dies für mich zu tun. In jedem Fall ist hier eine andere Option, wenn jemand interessiert ist:

Die Methode

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

Leistungstest

Dies war nicht im entferntesten wissenschaftlich. Ich kann mir vorstellen, dass Ihre Datenbankstruktur und die Anzahl der in der Liste enthaltenen IDs erhebliche Auswirkungen haben würden.

Ich habe einen Test eingerichtet, bei dem jeweils 100 Versuche durchgeführt wurden Concatund bei dem Containsbei jedem Versuch 25 Zeilen ausgewählt wurden, die durch eine zufällige Liste von Primärschlüsseln angegeben wurden. Ich habe das ungefähr ein Dutzend Mal ausgeführt, und meistens Concatkommt die Methode 5 - 10% schneller heraus, obwohl die ContainsMethode einmal nur von einem Smidgen gewonnen wurde.

DCShannon
quelle
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
quelle
-1

So mache ich es mit 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 ist im Grunde fast auf O (1), so dass Ihre Komplexität O (n) bleibt.

MG
quelle
Hier geht es um LINQ-to-SQL. Solche LINQ-to-Objects-Überlegungen gelten nicht.
Gert Arnold
ICollection kann auch von einem LINQ-SQL stammen, dies ist ein generischer Weg
MG
Die Frage ist, wie ein Ausdruck erstellt wird, der in korrektes SQL übersetzt wird. Das hat nichts damit zu tun, wie man eine lokale Sammlung durchsucht. Ihre Antwort wird nur zukünftige Leser täuschen, die sich dieser Unterscheidung nicht bewusst sind.
Gert Arnold