web-dev-qa-db-fra.com

Linq to SQL comment faire "où [colonne] dans (liste de valeurs)"

J'ai une fonction où j'obtiens une liste d'identifiants et je dois retourner la liste correspondant à une description associée à l'identifiant. Par exemple.:

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
}

Donc, si je créais moi-même le SQL pour cela, je ferais simplement quelque chose comme ceci (où la clause in contient toutes les valeurs de l'argument codeIds):

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

Dans Linq to Sql, je n'arrive pas à trouver l'équivalent de la clause "IN". Le meilleur que j'ai trouvé jusqu'à présent (qui ne fonctionne pas) est:

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

Le problème étant que je ne peux pas générer dynamiquement une liste de clauses "OR" pour linq to sql, car elles sont définies au moment de la compilation.

Comment peut-on accomplir une clause where qui vérifie qu'une colonne est dans une liste dynamique de valeurs en utilisant Linq to Sql? 

87
Nathan

Utilisation

where list.Contains(item.Property)

Ou dans votre cas:

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

Mais vous pourriez aussi bien faire cela en notation à points:

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

Vous pouvez aussi utiliser:

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;
23
Nick DeMayo

J'avais utilisé la méthode dans la réponse de Jon Skeet, mais une autre méthode m'est venue en utilisant Concat. La méthode Concat a légèrement mieux performé dans un test limité, mais c’est un casse-tête et je vais probablement rester fidèle à Contains, ou peut-être que j’écrirai une méthode auxiliaire pour le faire pour moi. De toute façon, voici une autre option si quelqu'un est intéressé:

La méthode

// 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 de performance

Ce n'était pas à distance scientifique. J'imagine que votre structure de base de données et le nombre d'identifiants impliqués dans la liste auraient un impact significatif.

J'ai mis en place un test dans lequel j'ai effectué 100 essais, chacun des Concat et Contains, dans lequel chaque essai impliquait la sélection de 25 lignes spécifiées par une liste aléatoire de clés primaires. Je l'ai exécuté environ une douzaine de fois, et la plupart du temps, la méthode Concat est 5 à 10% plus rapide, bien qu'une fois, la méthode Contains ait été remportée par un petit gagnant.

1
DCShannon
 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();
0
Deepan Raj