Come fare a SQL come% in Linq?


385

Ho una procedura in SQL che sto cercando di trasformare in Linq:

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

La linea di cui mi preoccupo di più è:

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

Ho una colonna che memorizza la gerarchia come / 1/3/12 / ad esempio, quindi uso solo% / 12 /% per cercarla.

La mia domanda è: qual è l'equivalente di Linq o .NET nell'uso del segno di percentuale?


1
La tua domanda ha almeno 5voti per il tag like-operator . Potrei richiedere gentilmente di suggerire sql-like come sinonimo ?
Kermit

Risposte:


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

Puoi anche usare .StartsWith()o .EndsWith().


4
L'uso di StartsWith () o EndsWith () genererà una query? Voglio dire, il codice verrà tradotto in una query o i risultati verranno filtrati nell'oggetto dopo il recupero dal DB?
Novizio

5
No. StartsWith () e EndsWith () fanno parte del predicato / filtro. L'esecuzione continua a essere rinviata.
andleer,

2
provato che ha ottenuto NullReferenceException: riferimento all'oggetto non impostato su un'istanza di un oggetto. Quindi non mi piace quando nel mio caso a.Address1.StartsWith (Address1) e a.Address1 è null
MikeT

11
StartsWith("abc")viene convertito in LIKE 'abc%'e EndsWith("abc")viene convertito inLIKE '%abc'
Simon_Weaver il

20
Non riuscivo a capire perché questo non funzionasse per un caso d'uso con le lettere, poi ho capito la mia stupidità ... non dimenticare .ToLower().Contains()ecc se vuoi ignorare il caso. Se lo desideri, ovviamente dipenderà dal fatto che cerchi di imitare LIKE da un DB con regole di confronto senza distinzione tra maiuscole e minuscole.
Adam Knights,

251

Usa questo:

from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;

22
questo è molto utile se si desidera utilizzare la corrispondenza del modello più complicata fornita dal comando like. Ad esempio, se si desidera verificare la presenza di due numeri (anziché 12), è possibile utilizzare questa espressione: SqlMethods.Like (c.Hierarchy, "% / [0-9] [0-9] /%") Inoltre , vedere questo msdn.microsoft.com/en-us/library/aa933232(SQL.80).aspx
viggity

questo è anche molto utile se si desidera consentire agli utenti esperti di precedere autonomamente il costoso% iniziale, laddove l'utilizzo di StartsWith o Contains non offre tale flessibilità all'utente
Simon_Weaver

8
Come si usa SqlMethodsusando la "notazione punto"?
dan-gph,

12
Si noti che è necessario includere lo System.Data.Linq.SqlClientspazio dei nomi.
johna,

1
Impossibile trovare System.Data.Linq.SqlClient sebbene sia possibile aggiungere System.Data.Linq. È deprecato?
Burak Karakuş,

41

Suppongo che tu stia usando Linq-to-SQL * (vedi nota sotto). In tal caso, utilizzare string.Contains, string.StartsWith e string.EndsWith per generare SQL che utilizza l'operatore 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 }

o

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

Nota: * = se si utilizza ADO.Net Entity Framework (EF / L2E) in .net 3.5, tenere presente che non eseguirà la stessa traduzione di Linq-to-SQL. Sebbene L2S esegua una traduzione corretta, L2E v1 (3.5) si tradurrà in un'espressione t-sql che imporrà una scansione completa della tabella sulla tabella che stai interrogando, a meno che non ci sia un altro migliore discriminatore nella clausola where o nei filtri join.
Aggiornamento: risolto in EF / L2E v4 (.net 4.0), quindi genererà un LIKE SQL proprio come fa L2S.


Non c'è bisogno di sfuggire alle tue corde con il @segno, ma mi rendo conto che questa potrebbe essere solo una buona convenzione da seguire.
andleer,

27

Se si utilizza VB.NET, la risposta sarebbe "*". Ecco come sarebbe la tua clausola where ...

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

Nota: "*" Corrisponde a zero o più caratteri. Ecco l'articolo msdn per l'operatore Like .


L'operatore VB Like si traduce in chiamate L2S? (Non ne ho idea.)
Andleer,

8
Sì, l'operatore VB Like viene tradotto nella versione SQL di like quando utilizzato in un'espressione di query LINQ. Inoltre, l'operatore VB Like non è limitato alle espressioni di query.
robertz,

1
Ho visto che esisteva al di fuori delle operazioni LINQ. Roba buona. +1
andleer

9

Bene indexOf funziona anche per me

var result = from c in SampleList
where c.LongName.IndexOf(SearchQuery) >= 0
select c;

1
Questa dovrebbe essere la risposta accettata. IndexOf si traduce in CHARINDEX in sql. Questo potrebbe essere più veloce di LIKE. Oltre a ciò, offre la possibilità di creare query di ricerca come '% some% thing%'. Dove 'some' deve essere posizionato prima di 'cosa', che non può essere fatto con Contains.
Ruard van Elburg,

Lo adoro quando le risposte di cui ho bisogno sono di 8 anni e nascoste più livelli sotto la risposta accettata. In parole povere, questo ha funzionato mentre .Contains (@ "/ 12 /") e altre risposte simili no. Molto apprezzato!
IdusOrtus,

4

Usa tale codice

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

4

.NET core ora ha EF.Functions.Like


Puoi spiegare come usarlo per risolvere il problema del PO?
Robert Columbia,

vedi cioè la risposta di LP, è solo la versione principale di SqlMethods
Come

Questa risposta dovrebbe contenere un esempio attuabile di come utilizzare questa funzione.
FoxDeploy

3

Nel caso in cui non abbini stringhe numeriche, è sempre bene avere un caso comune:

.Where(oh => oh.Hierarchy.ToUpper().Contains(mySearchString.ToUpper()))

2

Faccio sempre questo:

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

So di non usare l'istruzione like ma funziona benissimo in background se questo viene tradotto in una query con un'istruzione like.


In che modo la tua risposta è diversa dalla risposta accettata (risposta 7 anni fa) o da altre risposte? Quale valore aggiunge?
David Ferenczy Rogožan,

1
@DawidFerenczy Questa risposta funziona con la sintassi della query "from foo in bar" e quella accettata no.
nasch,

1

Prova questo, questo funziona bene per me

from record in context.Organization where record.Hierarchy.Contains(12) select record;


0

Contiene è usato in Linq, proprio come Like è usato in SQL.

string _search="/12/";

. . .

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

Puoi scrivere il tuo script SQL in Linq come segue:

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

0

Per quelli che sono qui come me che cercano un modo per un metodo "SQL Like" in LINQ, ho qualcosa che funziona molto bene.

Sono in un caso in cui non posso modificare il database in alcun modo per modificare le regole di confronto delle colonne. Quindi devo trovare un modo nel mio LINQ per farlo.

Sto usando il metodo helper SqlFunctions.PatIndexche agisce in modo simile al vero operatore SQL LIKE.

Per prima cosa ho bisogno di elencare tutti i possibili segni diacritici (una parola che ho appena imparato) nel valore di ricerca per ottenere qualcosa del tipo:

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

e poi in LINQ per esempio:

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

Quindi per le mie esigenze ho scritto un metodo 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 qualcuno di voi ha suggerimenti per migliorare questo metodo, sarò felice di ascoltarti.


Il tuo esempio è fondamentalmente un confronto insensibile all'accento fatto in casa. Una volta ho dovuto fare i conti con un progetto in cui ogni query passava attraverso un filtro per ottenere ciò che una corretta collazione avrebbe fatto automaticamente. Consultare stackoverflow.com/a/2461550/1736944 per quello che di solito è un approccio migliore. Assegnare le regole di confronto appropriate al database, alla tabella e / o al campo come ritenuto appropriato. (Lavorare senza un'adeguata collazione in atto è pura tortura)
Rune5

0

Molto tardi, ma ho combinato questo per essere in grado di fare confronti con String usando i caratteri jolly in stile SQL Like:

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;
    }
}
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.