Linq para SQL como fazer “where [coluna] in (lista de valores)”

101

Eu tenho uma função onde obtenho uma lista de ids e preciso retornar a uma lista que corresponda a uma descrição associada ao id. Por exemplo:

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
}

Portanto, se eu estivesse criando o sql para isso, simplesmente faria algo como o seguinte (em que a cláusula in contém todos os valores no argumento codeIds):

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

No Linq to Sql, não consigo encontrar o equivalente da cláusula "IN". O melhor que encontrei até agora (que não funciona) é:

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

O problema é que não consigo gerar dinamicamente uma lista de cláusulas "OR" de linq para sql, porque elas são definidas em tempo de compilação.

Como alguém realiza uma cláusula where que verifica se uma coluna está em uma lista dinâmica de valores usando Linq to Sql?

Nathan
fonte

Respostas:

159

Usar

where list.Contains(item.Property)

Ou no seu caso:

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

Mas você também pode fazer isso em notação de ponto:

var foo = channel.AsQueryable<CodeData>()
                 .Where(codeData => codeIDs.Contains(codeData.CodeId));
Jon Skeet
fonte
como usar no caso de CodeId ser Integer ??
Kiran Solkar
2
@KiranSolkar: Então, provavelmente codeIDsseria um List<int>, e tudo ficaria bem.
Jon Skeet
@JonSkeet Não diferencia maiúsculas de minúsculas? Se codeIDs for uma lista de strings em maiúsculas e codeData.codeId for uma string em letras minúsculas, ele falhará.
PersyJack
@PersyJack: Não havia nada na pergunta sobre não fazer distinção entre maiúsculas e minúsculas. Quanto a se seria ou não, não me lembro se o LINQ to SQL impõe a diferenciação de maiúsculas e minúsculas por padrão ou permite que as configurações de banco de dados controlem isso.
Jon Skeet
1
@PersyJack LINQ to SQL gera a consulta T-SQL, que então é executada no SQL Server usando as configurações do banco de dados para distinção entre maiúsculas e minúsculas. Embora, se alguém não tiver cuidado e materializar os resultados da consulta, antes de aplicar o LINQ aos objetos na memória, eles podem sofrer as consequências de diferenciação entre maiúsculas e minúsculas.
Zarepheth
26

Você também pode 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
fonte
1
Tive de usar isso, pois nossa implementação do IQToolkit não oferece suporte a .Contains ()
DJ van Wyk
1

Eu estava usando o método na resposta de Jon Skeet, mas outro me ocorreu usando Concat. O Concatmétodo funcionou um pouco melhor em um teste limitado, mas é um incômodo e provavelmente vou ficar comContains , ou talvez escreva um método auxiliar para fazer isso por mim. De qualquer forma, aqui está outra opção se alguém estiver interessado:

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

Teste de performance

Isso não era nem remotamente científico. Imagino que sua estrutura de banco de dados e o número de IDs envolvidos na lista teriam um impacto significativo.

Eu configurei um teste em que fiz 100 tentativas cada Concate em Containsque cada tentativa envolvia a seleção de 25 linhas especificadas por uma lista aleatória de chaves primárias. Eu executei isso cerca de uma dúzia de vezes, e na maioria das vezes o Concatmétodo sai 5 a 10% mais rápido, embora uma vez o Containsmétodo tenha vencido por apenas um pouquinho.

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

Aqui está como faço isso 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 é basicamente quase O (1), então sua complexidade permanece O (n).

MG
fonte
Trata-se de LINQ-to-SQL. Essas considerações do LINQ para objetos não se aplicam.
Gert Arnold
ICollection pode vir de um LINQ-SQL também, esta é uma forma genérica
MG
A questão é como construir uma expressão que se traduza em SQL correto. Isso não tem nada a ver com como pesquisar uma coleção local. Sua resposta apenas iludirá futuros leitores que não estão cientes dessa distinção.
Gert Arnold