Comment faire SQL Like% dans Linq?

385

J'ai une procédure en SQL que j'essaye de transformer en 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 ligne qui me préoccupe le plus est:

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

J'ai une colonne qui stocke la hiérarchie comme / 1/3/12 / par exemple, donc j'utilise simplement% / 12 /% pour la rechercher.

Ma question est, quel est l'équivalent Linq ou .NET à utiliser le signe pourcentage?

Matt Dell
la source
1
Votre question a au moins des 5votes pour le tag d' opérateur similaire . Puis-je vous demander de suggérer comme sql-like comme synonyme ?
Kermit

Réponses:

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

Vous pouvez également utiliser .StartsWith()ou .EndsWith().

andleer
la source
4
Est-ce que l'utilisation de StartsWith () ou EndsWith () déclenchera une requête? Je veux dire, le code sera-t-il traduit en une requête ou les résultats seront-ils filtrés dans l'objet après la récupération de la base de données?
Novice
5
Non. StartsWith () et EndsWith () font partie du prédicat / filtre. L'exécution continue d'être différée.
andleer
2
essayé qui a obtenu NullReferenceException: la référence d'objet n'est pas définie sur une instance d'un objet. Donc, ça n'aime pas quand dans mon cas a.Address1.StartsWith (Address1) et a.Address1 est nul
MikeT
11
StartsWith("abc")se convertit LIKE 'abc%'et EndsWith("abc")est converti enLIKE '%abc'
Simon_Weaver
20
Impossible de comprendre pourquoi cela ne fonctionnait pas pour un cas d'utilisation avec des lettres, puis j'ai réalisé ma stupidité ... n'oubliez pas, .ToLower().Contains()etc. si vous voulez ignorer la casse. Si vous le souhaitez, cela dépendra bien sûr si vous essayez d'imiter LIKE à partir d'une base de données avec un classement insensible à la casse ou non.
Adam Knights
251

Utilisez ceci:

from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;
LP
la source
22
ceci est vraiment utile si vous souhaitez utiliser la correspondance de motifs plus compliquée fournie par la commande like. Par exemple, si vous souhaitez rechercher deux nombres (au lieu de 12), vous pouvez utiliser cette expression: SqlMethods.Like (c.Hierarchy, "% / [0-9] [0-9] /%") , consultez ce msdn.microsoft.com/en-us/library/aa933232(SQL.80).aspx
viggity
cela est également très utile si vous voulez permettre aux utilisateurs expérimentés de prévoir eux-mêmes le% initial coûteux, où l'utilisation de StartsWith ou Contains ne donne pas à l'utilisateur
expérimenté
8
Comment utilisez-vous en SqlMethodsutilisant la «notation par points»?
dan-gph
12
Notez que vous devez inclure l' System.Data.Linq.SqlClientespace de noms.
johna
1
Je n'ai pas pu trouver System.Data.Linq.SqlClient bien que je puisse ajouter System.Data.Linq. Est-il obsolète?
Burak Karakuş
41

Je suppose que vous utilisez Linq-to-SQL * (voir la note ci-dessous). Si tel est le cas, utilisez string.Contains, string.StartsWith et string.EndsWith pour générer du SQL qui utilise l'opérateur 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 }

Remarque: * = si vous utilisez ADO.Net Entity Framework (EF / L2E) dans .net 3.5, sachez qu'il ne fera pas la même traduction que Linq-to-SQL. Bien que L2S effectue une traduction appropriée, L2E v1 (3.5) se traduira par une expression t-sql qui forcera une analyse complète de la table sur la table que vous interrogez, sauf s'il existe un autre meilleur discriminateur dans votre clause where ou vos filtres de jointure.
Mise à jour: Ceci est corrigé dans EF / L2E v4 (.net 4.0), donc il générera un SQL LIKE tout comme L2S.

KristoferA
la source
Pas besoin d'échapper à vos cordes avec le @signe mais je me rends compte que cela peut être une bonne convention à suivre.
andleer
27

Si vous utilisez VB.NET, la réponse serait "*". Voici à quoi ressemblerait votre clause where ...

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

Remarque: "*" Correspond à zéro ou plusieurs caractères. Voici l'article msdn pour l'opérateur Like .

robertz
la source
L'opérateur VB Like se traduit-il en appels L2S? (Je n'en ai aucune idée.)
andleer
8
Oui, l'opérateur VB Like est traduit dans la version SQL de like lorsqu'il est utilisé dans une expression de requête LINQ. En outre, l'opérateur VB Like n'est pas limité aux expressions de requête.
robertz
1
J'ai vu qu'il existait en dehors des opérations LINQ. Bon produit. +1
andleer
9

Eh bien, l'index fonctionne aussi pour moi

var result = from c in SampleList
where c.LongName.IndexOf(SearchQuery) >= 0
select c;
Rumplin
la source
1
Ce devrait être la réponse acceptée. IndexOf se traduit par CHARINDEX en sql. Cela peut être plus rapide que LIKE. Mais à part cela, cela donne la possibilité de construire des requêtes de recherche comme '% some% thing%'. Où «certains» doit être situé avant «chose», ce qui ne peut pas être fait avec Contains.
Ruard van Elburg
J'adore quand les réponses dont j'ai besoin ont 8 ans et se trouvent à plusieurs niveaux sous la réponse acceptée. En termes simples, cela a fonctionné alors que .Contains (@ "/ 12 /") et d'autres réponses similaires ne l'ont pas fait. Très appréciée!
IdusOrtus
4

Utilisez un tel code

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
la source
4

Le noyau .NET a maintenant EF.Functions.Like

kofifus
la source
Pouvez-vous expliquer comment l'utiliser pour résoudre le problème du PO?
Robert Columbia
voir ie la réponse de LP, c'est juste la version de base de SqlMethods.Like
kofifus
Cette réponse doit contenir un exemple exploitable de la façon d'utiliser cette fonction.
FoxDeploy
3

Dans le cas où vous ne correspondez pas à des chaînes numériques, il est toujours bon d'avoir une casse commune:

.Where(oh => oh.Hierarchy.ToUpper().Contains(mySearchString.ToUpper()))
Entrez
la source
2

Je fais toujours ça:

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

Je sais que je n'utilise pas la même déclaration, mais cela fonctionne très bien en arrière-plan est-ce traduit en une requête avec une déclaration similaire.

H. Pauwelyn
la source
En quoi votre réponse est-elle différente de la réponse acceptée (réponse il y a 7 ans) ou d'autres réponses? Quelle valeur ajoute-t-elle?
David Ferenczy Rogožan
1
@DawidFerenczy Cette réponse fonctionne avec la syntaxe de requête "from foo in bar", et pas celle acceptée.
nasch
1

Essayez ceci, cela fonctionne bien pour moi

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

Contient est utilisé dans Linq, tout comme Like est utilisé dans SQL.

string _search="/12/";

. . .

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

Vous pouvez écrire votre script SQL dans Linq comme suit:

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

Pour ceux qui dégringolent ici comme moi à la recherche d'un moyen pour une méthode "SQL Like" dans LINQ, j'ai quelque chose qui fonctionne très bien.

Je suis dans un cas où je ne peux en aucun cas modifier la base de données pour modifier le classement des colonnes. Je dois donc trouver un moyen dans mon LINQ de le faire.

J'utilise la méthode d'assistance SqlFunctions.PatIndexqui agit de la même manière que le véritable opérateur SQL LIKE.

J'ai d'abord besoin d'énumérer tous les signes diacritiques possibles (un mot que je viens d'apprendre) dans la valeur de recherche pour obtenir quelque chose comme:

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

puis dans LINQ par exemple:

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

Donc, pour mes besoins, j'ai écrit une méthode 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;
        }
    }

Si vous avez des suggestions pour améliorer cette méthode, je serai ravi de vous entendre.

Hugo
la source
Votre exemple est essentiellement un classement insensible aux accents homebrewed. Une fois, j'ai dû gérer un projet dans lequel chaque requête passait par un filtre pour obtenir ce qu'un classement correct aurait fait automatiquement. Veuillez consulter stackoverflow.com/a/2461550/1736944 pour ce qui est généralement une meilleure approche. Attribuez le classement approprié à la base de données, à la table et / ou au champ, selon ce qui est jugé approprié. (Travailler sans une collation appropriée en place est une pure torture)
9Rune5
0

Bien en retard, mais j'ai jeté cela ensemble pour pouvoir faire des comparaisons de chaînes en utilisant des caractères génériques de style 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
la source