Como fazer o SQL Like% no Linq?

385

Eu tenho um procedimento no SQL que estou tentando transformar em Linq:

SELECT O.Id, O.Name as Organization
FROM Organizations O
JOIN OrganizationsHierarchy OH ON O.Id=OH.OrganizationsId
where OH.Hierarchy like '%/12/%'

A linha que mais me preocupa é:

where OH.Hierarchy like '%/12/%'

Eu tenho uma coluna que armazena a hierarquia como / 1/3/12 / por exemplo, então eu apenas uso% / 12 /% para procurá-la.

Minha pergunta é: qual é o equivalente do Linq ou .NET para usar o sinal de porcentagem?

Matt Dell
fonte
11
Sua pergunta tem pelo menos 5votos para a tag de operador like . Posso pedir que você sugira sql-like como sinônimo ?
Caco

Respostas:

550
.Where(oh => oh.Hierarchy.Contains("/12/"))

Você também pode usar .StartsWith()ou .EndsWith().

andleer
fonte
4
Usar StartsWith () ou EndsWith () disparará uma consulta? Quero dizer, o código será traduzido em uma consulta ou os resultados serão filtrados no objeto após a recuperação do banco de dados?
Novato
5
Não. StartsWith () e EndsWith () fazem parte do predicado / filtro. A execução continua sendo adiada.
andleer
2
tentou que obteve NullReferenceException: referência de objeto não definida para uma instância de um objeto. Por isso, não gosto quando no meu caso a.Address1.StartsWith (Address1) e a.Address1 é nulo
miket
11
StartsWith("abc")é convertido em LIKE 'abc%'e EndsWith("abc")é cnoverted paraLIKE '%abc'
Simon_Weaver
20
Não foi possível descobrir por que isso não estava funcionando para um caso de uso com letras, então percebi minha estupidez ... não se esqueça, .ToLower().Contains()etc., se você quiser ignorar o caso. Se você deseja isso, é claro, dependerá de sua tentativa de imitar o LIKE de um banco de dados com agrupamento sem distinção entre maiúsculas e minúsculas ou não.
Adam Knights
251

Usa isto:

from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;
LP
fonte
22
isso é realmente útil se você deseja usar a correspondência de padrões mais complicada fornecida pelo comando like. Por exemplo, se você quiser procurar por dois números (em vez de 12), poderá usar esta expressão: SqlMethods.Like (c.Hierarchy, "% / [0-9] [0-9] /%") também , consulte este msdn.microsoft.com/en-us/library/aa933232(SQL.80).aspx
viggity
Isso também é muito útil se você quiser permitir que os usuários de energia à pré-PEND o% inicial caro si, onde o uso de StartsWith ou Contém não dá ao usuário o poder esta flexibilidade
Simon_Weaver
8
Como você usa o SqlMethodsuso da "notação de ponto"?
Dan-gph
12
Observe que você precisa incluir o System.Data.Linq.SqlClientespaço para nome.
johna
11
Não consegui encontrar System.Data.Linq.SqlClient, embora eu possa adicionar System.Data.Linq. Está obsoleto?
Burak Karakuş
41

Suponho que você esteja usando o Linq-to-SQL * (veja a nota abaixo). Nesse caso, use string.Contains, string.StartsWith e string.EndsWith para gerar SQL que use o operador SQL LIKE.

from o in dc.Organization
join oh in dc.OrganizationsHierarchy on o.Id equals oh.OrganizationsId
where oh.Hierarchy.Contains(@"/12/")
select new { o.Id, o.Name }

ou

from o in dc.Organization
where o.OrganizationsHierarchy.Hierarchy.Contains(@"/12/")
select new { o.Id, o.Name }

Nota: * = se você estiver usando o ADO.Net Entity Framework (EF / L2E) no .net 3.5, saiba que ele não fará a mesma tradução que o Linq-to-SQL. Embora o L2S faça uma tradução adequada, o L2E v1 (3.5) se traduzirá em uma expressão t-sql que forçará uma verificação completa da tabela na tabela que você está consultando, a menos que haja outro discriminador melhor na sua cláusula where ou nos filtros de associação.
Atualização: Isso é corrigido no EF / L2E v4 (.net 4.0), portanto, gera um SQL LIKE exatamente como o L2S.

KristoferA
fonte
Não há necessidade de escapar das cordas com o @sinal, mas sei que isso pode ser apenas uma boa convenção a seguir.
andleer
27

Se você estiver usando o VB.NET, a resposta seria "*". Aqui está a aparência da sua cláusula where ...

Where OH.Hierarchy Like '*/12/*'

Nota: "*" Corresponde a zero ou mais caracteres. Aqui está o artigo msdn para o operador Like .

robertz
fonte
O operador VB Like se traduz em chamadas L2S? (Eu não tenho idéia.)
andleer
8
Sim, o operador VB Like é convertido para a versão SQL do like quando usado em uma expressão de consulta LINQ. Além disso, o operador VB Like não está restrito a expressões de consulta.
Robertz
11
Vi que ele existia fora das operações do LINQ. Coisa boa. +1
andleer
9

Bem indexOf funciona para mim também

var result = from c in SampleList
where c.LongName.IndexOf(SearchQuery) >= 0
select c;
Rumplin
fonte
11
Essa deve ser a resposta aceita. IndexOf traduz para CHARINDEX em sql. Isso pode ser mais rápido que LIKE. Além disso, ele oferece a possibilidade de criar consultas de pesquisa como '% some% thing%'. Onde 'some' deve estar localizado antes de 'thing', o que não pode ser feito com Contains.
Ruard van Elburg
Adoro quando as respostas necessárias precisam ter 8 anos e colocar várias camadas abaixo da resposta aceita. Simplificando, isso funcionou, enquanto. Contém (@ "/ 12 /") e outras respostas semelhantes não. Muito apreciado!
IdusOrtus 24/05/19
4

Use esse código

try
{
    using (DatosDataContext dtc = new DatosDataContext())
    {
        var query = from pe in dtc.Personal_Hgo
                    where SqlMethods.Like(pe.nombre, "%" + txtNombre.Text + "%")
                    select new
                    {
                        pe.numero
                        ,
                        pe.nombre
                    };
        dgvDatos.DataSource = query.ToList();
    }
}
catch (Exception ex)
{
    string mensaje = ex.Message;
}
Ernesto
fonte
4

O núcleo do .NET agora tem EF.Functions.Like

kofifus
fonte
Você pode explicar como usar isso para resolver o problema do OP?
Robert Columbia
Veja, ou seja, a resposta do LP, é apenas a versão principal do SqlMethods.Like
kofifus
Esta resposta deve conter um exemplo acionável de como usar esta função.
usar o seguinte comando
3

Caso você não esteja combinando cadeias numéricas, é sempre bom ter maiúsculas e minúsculas:

.Where(oh => oh.Hierarchy.ToUpper().Contains(mySearchString.ToUpper()))
Entre
fonte
2

Eu sempre faço isso:

from h in OH
where h.Hierarchy.Contains("/12/")
select h

Eu sei que não uso a instrução like, mas funciona bem em segundo plano. Isso é traduzido em uma consulta com uma instrução like.

H. Pauwelyn
fonte
Qual é a sua resposta diferente da resposta aceita (respondida 7 anos atrás) ou de outras respostas? Que valor isso acrescenta?
David Ferenczy Rogožan
11
@DawidFerenczy Esta resposta funciona com a sintaxe de consulta "from foo in bar", e a aceita não.
Nasch
1

Tente isso, isso funciona bem para mim

from record in context.Organization where record.Hierarchy.Contains(12) select record;
isuruAb
fonte
1
System.Data.Linq.SqlClient.SqlMethods.Like("mystring", "%string")
Tan Silliksaar
fonte
0

Contém é usado no Linq, assim como é usado no SQL.

string _search="/12/";

. . .

.Where(s => s.Hierarchy.Contains(_search))

Você pode escrever seu script SQL no Linq da seguinte forma:

 var result= Organizations.Join(OrganizationsHierarchy.Where(s=>s.Hierarchy.Contains("/12/")),s=>s.Id,s=>s.OrganizationsId,(org,orgH)=>new {org,orgH});
UJS
fonte
0

Para aqueles que caem aqui como eu procurando um método "SQL Like" no LINQ, tenho algo que está funcionando muito bem.

Estou em um caso em que não posso alterar o banco de dados de forma alguma para alterar o agrupamento de colunas. Então, eu tenho que encontrar uma maneira no meu LINQ para fazer isso.

Estou usando o método auxiliar SqlFunctions.PatIndexque age de maneira semelhante ao operador SQL LIKE real.

Primeiro, preciso enumerar todos os diacríticos possíveis (uma palavra que acabei de aprender) no valor da pesquisa para obter algo como:

déjà     => d[éèêëeÉÈÊËE]j[aàâäAÀÂÄ]
montreal => montr[éèêëeÉÈÊËE][aàâäAÀÂÄ]l
montréal => montr[éèêëeÉÈÊËE][aàâäAÀÂÄ]l

e depois no LINQ, por exemplo:

var city = "montr[éèêëeÉÈÊËE][aàâäAÀÂÄ]l";
var data = (from loc in _context.Locations
                     where SqlFunctions.PatIndex(city, loc.City) > 0
                     select loc.City).ToList();

Então, para as minhas necessidades, escrevi um método Helper / Extension

   public static class SqlServerHelper
    {

        private static readonly List<KeyValuePair<string, string>> Diacritics = new List<KeyValuePair<string, string>>()
        {
            new KeyValuePair<string, string>("A", "aàâäAÀÂÄ"),
            new KeyValuePair<string, string>("E", "éèêëeÉÈÊËE"),
            new KeyValuePair<string, string>("U", "uûüùUÛÜÙ"),
            new KeyValuePair<string, string>("C", "cçCÇ"),
            new KeyValuePair<string, string>("I", "iîïIÎÏ"),
            new KeyValuePair<string, string>("O", "ôöÔÖ"),
            new KeyValuePair<string, string>("Y", "YŸÝýyÿ")
        };

        public static string EnumarateDiacritics(this string stringToDiatritics)
        {
            if (string.IsNullOrEmpty(stringToDiatritics.Trim()))
                return stringToDiatritics;

            var diacriticChecked = string.Empty;

            foreach (var c in stringToDiatritics.ToCharArray())
            {
                var diac = Diacritics.FirstOrDefault(o => o.Value.ToCharArray().Contains(c));
                if (string.IsNullOrEmpty(diac.Key))
                    continue;

                //Prevent from doing same letter/Diacritic more than one time
                if (diacriticChecked.Contains(diac.Key))
                    continue;

                diacriticChecked += diac.Key;

                stringToDiatritics = stringToDiatritics.Replace(c.ToString(), "[" + diac.Value + "]");
            }

            stringToDiatritics = "%" + stringToDiatritics + "%";
            return stringToDiatritics;
        }
    }

Se algum de vocês tiver alguma sugestão para aprimorar esse método, ficarei feliz em ouvi-lo.

Hugo
fonte
Seu exemplo é basicamente um agrupamento insensível ao sotaque produzido em casa. Uma vez tive que lidar com um projeto em que todas as consultas passavam por um filtro para obter o que um agrupamento adequado teria feito automaticamente. Consulte stackoverflow.com/a/2461550/1736944 para saber o que geralmente é uma abordagem melhor. Atribua o agrupamento adequado ao banco de dados, tabela e / ou campo, conforme apropriado. (Trabalhar sem um agrupamento adequado no lugar é pura tortura)
9Rune5
0

Muito tarde, mas eu juntei isso para poder fazer comparações de String usando caracteres curinga do estilo SQL:

public static class StringLikeExtensions
{
    /// <summary>
    /// Tests a string to be Like another string containing SQL Like style wildcards
    /// </summary>
    /// <param name="value">string to be searched</param>
    /// <param name="searchString">the search string containing wildcards</param>
    /// <returns>value.Like(searchString)</returns>
    /// <example>value.Like("a")</example>
    /// <example>value.Like("a%")</example>
    /// <example>value.Like("%b")</example>
    /// <example>value.Like("a%b")</example>
    /// <example>value.Like("a%b%c")</example>
    /// <remarks>base author -- Ruard van Elburg from StackOverflow, modifications by dvn</remarks>
    /// <remarks>converted to a String extension by sja</remarks>
    /// <seealso cref="/programming/1040380/wildcard-search-for-linq"/>
    public static bool Like(this String value, string searchString)
    {
        bool result = false;

        var likeParts = searchString.Split(new char[] { '%' });

        for (int i = 0; i < likeParts.Length; i++)
        {
            if (likeParts[i] == String.Empty)
            {
                continue;   // "a%"
            }

            if (i == 0)
            {
                if (likeParts.Length == 1) // "a"
                {
                    result = value.Equals(likeParts[i], StringComparison.OrdinalIgnoreCase);
                }
                else // "a%" or "a%b"
                {
                    result = value.StartsWith(likeParts[i], StringComparison.OrdinalIgnoreCase);
                }
            }
            else if (i == likeParts.Length - 1) // "a%b" or "%b"
            {
                result &= value.EndsWith(likeParts[i], StringComparison.OrdinalIgnoreCase);
            }
            else // "a%b%c"
            {
                int current = value.IndexOf(likeParts[i], StringComparison.OrdinalIgnoreCase);
                int previous = value.IndexOf(likeParts[i - 1], StringComparison.OrdinalIgnoreCase);
                result &= previous < current;
            }
        }

        return result;
    }

    /// <summary>
    /// Tests a string containing SQL Like style wildcards to be ReverseLike another string 
    /// </summary>
    /// <param name="value">search string containing wildcards</param>
    /// <param name="compareString">string to be compared</param>
    /// <returns>value.ReverseLike(compareString)</returns>
    /// <example>value.ReverseLike("a")</example>
    /// <example>value.ReverseLike("abc")</example>
    /// <example>value.ReverseLike("ab")</example>
    /// <example>value.ReverseLike("axb")</example>
    /// <example>value.ReverseLike("axbyc")</example>
    /// <remarks>reversed logic of Like String extension</remarks>
    public static bool ReverseLike(this String value, string compareString)
    {
        bool result = false;

        var likeParts = value.Split(new char[] {'%'});

        for (int i = 0; i < likeParts.Length; i++)
        {
            if (likeParts[i] == String.Empty)
            {
                continue;   // "a%"
            }

            if (i == 0)
            {
                if (likeParts.Length == 1) // "a"
                {
                    result = compareString.Equals(likeParts[i], StringComparison.OrdinalIgnoreCase);
                }
                else // "a%" or "a%b"
                {
                    result = compareString.StartsWith(likeParts[i], StringComparison.OrdinalIgnoreCase);
                }
            }
            else if (i == likeParts.Length - 1) // "a%b" or "%b"
            {
                result &= compareString.EndsWith(likeParts[i], StringComparison.OrdinalIgnoreCase);
            }
            else // "a%b%c"
            {
                int current = compareString.IndexOf(likeParts[i], StringComparison.OrdinalIgnoreCase);
                int previous = compareString.IndexOf(likeParts[i - 1], StringComparison.OrdinalIgnoreCase);
                result &= previous < current;
            }
        }

        return result;
    }
}
Steven Ackerman
fonte