411

I have a procedure in SQL that I am trying to turn into Linq:

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

The line I am most concerned with is:

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

I have a column that stores the hierarchy like /1/3/12/ for example so I just use %/12/% to search for it.

My question is, what is the Linq or .NET equivalent to using the percent sign?

Matt Dell
  • 9,205
  • 11
  • 41
  • 58
  • 1
    Your question has at least `5` votes for the [tag:like-operator] tag. Could I kindly request that you suggest [tag:sql-like] as a [synonym](http://stackoverflow.com/tags/like-operator/synonyms)? – Kermit Apr 02 '13 at 18:36
  • It's not clear if a LINQ-to-Objects equivalent is looked for. If, so, the linq-to-entities tag isn't relevant and even confusing. – Gert Arnold Jan 30 '21 at 22:56

15 Answers15

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

You can also use .StartsWith() or .EndsWith().

jwheron
  • 2,553
  • 2
  • 30
  • 40
andleer
  • 22,388
  • 8
  • 62
  • 82
  • 5
    Will using StartsWith() or EndsWith() will fire a query ? I mean, will the code be translated into a Query or the results will be filtered in the object after retrieval from the DB ? – Novice Sep 18 '12 at 06:25
  • 5
    No. StartsWith() and EndsWith() are part of the predicate / filter. Execution continues to be deferred. – andleer Sep 18 '12 at 16:08
  • 2
    tried that got NullReferenceException: Object reference not set to an instance of an object. So it doesn't like it when in my case a.Address1.StartsWith(Address1) and a.Address1 is null – MikeT Jan 25 '13 at 17:33
  • Correct. That has nothing to do with StartsWidth. Your dependent object `Address1` doesn't exist in your join. – andleer Jan 25 '13 at 17:46
  • yep got round it by using several where clauses wrapped in null checks so: if(string.isnullorempty(searchField)) results = results.where(a => a.SearchField.startswith(searchField) – MikeT Jan 28 '13 at 13:09
  • @andleer if the word like 'Data' and I am searching using 'da', then it will not retrieve the correct records.Can you please suggest any way ? – gofor.net Apr 04 '13 at 09:35
  • does searching on "da" work? If so, you install of SQL may be case sensitive. Take a look at this: http://stackoverflow.com/questions/1439485/how-to-determine-if-an-instance-of-sql-server-is-case-sensitive-or-not – andleer Apr 04 '13 at 17:46
  • 12
    `StartsWith("abc")` gets converted into `LIKE 'abc%'` and `EndsWith("abc")` is cnoverted to `LIKE '%abc'` – Simon_Weaver Aug 09 '13 at 04:37
  • 25
    Couldn't work out why this wasn't working for a use case with letters, then realized my stupidity... don't forget `.ToLower().Contains()` etc if you want to ignore case. Whether you want this will of course depend on whether your trying to mimic LIKE from a DB with case insensitive collation or not. – Adam Knights Feb 11 '14 at 11:52
  • 1
    I like `L P`'s answer more. In some instances you cannot do without that solution – Pierre Sep 19 '14 at 17:35
  • @Simon_Weaver - That is only true when using Linq-to-SQL syntax, otherwise you get something like this: (( CAST(CHARINDEX([Extent1].[HierarchyID], @p__linq__0) AS int)) = 1) – influent Jan 21 '16 at 00:02
  • 1
    So, How to do that? A like 'wo%ld' for world – Savas Adar Jun 10 '16 at 11:35
  • @SavasAdar take a look here: http://stackoverflow.com/questions/1040380/wildcard-search-for-linq/42307642#42307642 –  Feb 17 '17 at 21:59
  • I'm still having the issue with .StartsWith(). I posted it here: https://github.com/praeclarum/sqlite-net/issues/696 – Euridice01 Feb 14 '18 at 17:09
  • What about "%abc%xyz%" ?How do I express this in linq ? – Beetlejuice Aug 09 '18 at 20:10
262

Use this:

from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;
L P
  • 2,629
  • 1
  • 15
  • 2
  • 23
    this is really helpful if you want to use the more complicated pattern matching provided by the like command. For instance, if you wanted to check for any two numbers (instead of 12), you could use this expression: SqlMethods.Like(c.Hierarchy, "%/[0-9][0-9]/%") Also, see this http://msdn.microsoft.com/en-us/library/aa933232(SQL.80).aspx – viggity Dec 08 '10 at 15:20
  • 1
    this is also very useful if you want to allow power users to pre-pend the expensive initial % themselves, where using StartsWith or Contains doesn't give the power user this flexibility – Simon_Weaver Aug 09 '13 at 04:20
  • 9
    How do you use `SqlMethods` using "dot notation"? – dan-gph Apr 15 '14 at 02:15
  • 16
    Note that you need to include the `System.Data.Linq.SqlClient` namespace. – johna Jul 28 '14 at 23:46
  • 3
    I couldn't find System.Data.Linq.SqlClient although I can add System.Data.Linq. Is it deprecated? – Burak Karakuş Dec 10 '15 at 09:08
  • @BurakKarakuş It works for me. Are you missing a reference for `System.Data.Linq`? – user2428118 Dec 17 '15 at 13:51
  • @user2428118 no I am not because System.Data.Linq is already referenced and used in other parts of the code. – Burak Karakuş Dec 17 '15 at 14:01
43

I'm assuming you're using Linq-to-SQL* (see note below). If so, use string.Contains, string.StartsWith, and string.EndsWith to generate SQL that use the SQL LIKE operator.

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 }

or

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

Note: * = if you are using the ADO.Net Entity Framework (EF / L2E) in .net 3.5, be aware that it will not do the same translation as Linq-to-SQL. Although L2S does a proper translation, L2E v1 (3.5) will translate into a t-sql expression that will force a full table scan on the table you're querying unless there is another better discriminator in your where clause or join filters.
Update: This is fixed in EF/L2E v4 (.net 4.0), so it will generate a SQL LIKE just like L2S does.

KristoferA
  • 12,287
  • 1
  • 40
  • 62
  • No need to escape your strings with the `@` sign but I realize this may just be a good convention to follow. – andleer Oct 03 '13 at 15:42
29

If you are using VB.NET, then the answer would be "*". Here is what your where clause would look like...

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

Note: "*" Matches zero or more characters. Here is the msdn article for the Like operator.

robertz
  • 758
  • 6
  • 11
  • Does the VB Like operator translate into L2S calls? (I have no idea.) – andleer May 07 '09 at 18:30
  • 8
    Yes, the VB Like operator gets translated to the SQL version of like when used in a LINQ query expression. Also, the VB Like operator is not restricted to query expressions. – robertz May 07 '09 at 19:06
  • 1
    I saw that it existed outside of LINQ operations. Good stuff. +1 – andleer May 07 '09 at 20:28
8

Well indexOf works for me too

var result = from c in SampleList
where c.LongName.IndexOf(SearchQuery) >= 0
select c;
Rumplin
  • 2,703
  • 21
  • 45
  • 1
    This should be the accepted answer. IndexOf translates to CHARINDEX in sql. This may possibly be faster than LIKE. But apart from that, it gives the possiblity to construct search queries like '%some%thing%'. Where 'some' has to be located before 'thing', which can't be done with Contains. –  Feb 17 '17 at 22:03
  • 1
    I love it when the answers I need are 8 years old and tucked several tiers beneath the accepted answer. Put simply, this worked whereas .Contains(@"/12/") and other similar answers did not. Much appreciated! – IdusOrtus May 24 '19 at 20:46
7

.NET core now has EF.Functions.Like

  var isMatch = EF.Functions.Like(stringThatMightMatch, pattern);
R.D. Alkire
  • 502
  • 1
  • 5
  • 14
kofifus
  • 17,260
  • 17
  • 99
  • 173
4

Use such 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;
}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
Ernesto
  • 49
  • 1
  • 1
3

In case you are not matching numeric strings, always good to have common case:

.Where(oh => oh.Hierarchy.ToUpper().Contains(mySearchString.ToUpper()))
ComeIn
  • 1,519
  • 17
  • 12
2

I do always this:

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

I know I don't use the like statement but it's work fine in the background is this translated into a query with a like statement.

H. Pauwelyn
  • 13,575
  • 26
  • 81
  • 144
2
System.Data.Linq.SqlClient.SqlMethods.Like("mystring", "%string")
Tan Silliksaar
  • 169
  • 1
  • 1
  • 10
1

Try this, this works fine for me

from record in context.Organization where record.Hierarchy.Contains(12) select record;
isuruAb
  • 2,202
  • 5
  • 26
  • 39
1

For those how tumble here like me looking for a way to a "SQL Like" method in LINQ, I've something that is working very good.

I'm in a case where I cannot alter the Database in any way to change the column collation. So I've to find a way in my LINQ to do it.

I'm using the helper method SqlFunctions.PatIndex witch act similarly to the real SQL LIKE operator.

First I need enumerate all possible diacritics (a word that I just learned) in the search value to get something like:

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

and then in LINQ for 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();

So for my needs I've written a Helper/Extension method

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

If any of you have suggestion to enhance this method, I'll be please to hear you.

Hugo
  • 2,077
  • 2
  • 28
  • 34
  • Your example is basically a homebrewed accent insensitive collation. I once had to deal with a project where each and every query went through a filter to achieve what a proper collation would have done automatically. Please see https://stackoverflow.com/a/2461550/1736944 for what is usually a better approach. Assign the proper collation to the database, table and/or field as deemed appropriate. (Working without a proper collation in place is pure torture) – 9Rune5 Jan 03 '19 at 11:55
1

Way late, but I threw this together to be able to do String comparisons using SQL Like style wildcards:

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="https://stackoverflow.com/questions/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;
    }
}
  • This is perfect! I needed this function to use in a Unit Test with Moq, but all other answers needed a server or simply didn't match. – Robin GM Mar 09 '22 at 10:01
0

Contains is used in Linq ,Just like Like is used in SQL .

string _search="/12/";

. . .

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

You can write your SQL script in Linq as Following :

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

If you need the LIKE functionality for a client operation like Unit Testing, this method by CodeProject mimics the behavior of wildcards well.

A bit like @Steve Ackerman's answer, but more comprehensive.

/// Published on CodeProject: http://www.codeproject.com/Articles/
///         608266/A-Csharp-LIKE-implementation-that-mimics-SQL-LIKE
/// </remarks>
public static bool Like(this string s, string match, bool CaseInsensitive = true)
{
    //Nothing matches a null mask or null input string
    if (match == null || s == null)
        return false;
    //Null strings are treated as empty and get checked against the mask.
    //If checking is case-insensitive we convert to uppercase to facilitate this.
    if (CaseInsensitive)
    {
        s = s.ToUpperInvariant();
        match = match.ToUpperInvariant();
    }
    //Keeps track of our position in the primary string - s.
    int j = 0;
    //Used to keep track of multi-character wildcards.
    bool matchanymulti = false;
    //Used to keep track of multiple possibility character masks.
    string multicharmask = null;
    bool inversemulticharmask = false;
    for (int i = 0; i < match.Length; i++)
    {
        //If this is the last character of the mask and its a % or * we are done
        if (i == match.Length - 1 && (match[i] == '%' || match[i] == '*'))
            return true;
        //A direct character match allows us to proceed.
        var charcheck = true;
        //Backslash acts as an escape character.  If we encounter it, proceed
        //to the next character.
        if (match[i] == '\\')
        {
            i++;
            if (i == match.Length)
                i--;
        }
        else
        {
            //If this is a wildcard mask we flag it and proceed with the next character
            //in the mask.
            if (match[i] == '%' || match[i] == '*')
            {
                matchanymulti = true;
                continue;
            }
            //If this is a single character wildcard advance one character.
            if (match[i] == '_')
            {
                //If there is no character to advance we did not find a match.
                if (j == s.Length)
                    return false;
                j++;
                continue;
            }
            if (match[i] == '[')
            {
                var endbracketidx = match.IndexOf(']', i);
                //Get the characters to check for.
                multicharmask = match.Substring(i + 1, endbracketidx - i - 1);
                //Check for inversed masks
                inversemulticharmask = multicharmask.StartsWith("^");
                //Remove the inversed mask character
                if (inversemulticharmask)
                    multicharmask = multicharmask.Remove(0, 1);
                //Unescape \^ to ^
                multicharmask = multicharmask.Replace("\\^", "^");
                
                //Prevent direct character checking of the next mask character
                //and advance to the next mask character.
                charcheck = false;
                i = endbracketidx;
                //Detect and expand character ranges
                if (multicharmask.Length == 3 && multicharmask[1] == '-')
                {
                    var newmask = "";
                    var first = multicharmask[0];
                    var last = multicharmask[2];
                    if (last < first)
                    {
                        first = last;
                        last = multicharmask[0];
                    }
                    var c = first;
                    while (c <= last)
                    {
                        newmask += c;
                        c++;
                    }
                    multicharmask = newmask;
                }
                //If the mask is invalid we cannot find a mask for it.
                if (endbracketidx == -1)
                    return false;
            }
        }
        //Keep track of match finding for this character of the mask.
        var matched = false;
        while (j < s.Length)
        {
            //This character matches, move on.
            if (charcheck && s[j] == match[i])
            {
                j++;
                matched = true;
                break;
            }
            //If we need to check for multiple charaters to do.
            if (multicharmask != null)
            {
                var ismatch = multicharmask.Contains(s[j]);
                //If this was an inverted mask and we match fail the check for this string.
                //If this was not an inverted mask check and we did not match fail for this string.
                if (inversemulticharmask && ismatch ||
                    !inversemulticharmask && !ismatch)
                {
                    //If we have a wildcard preceding us we ignore this failure
                    //and continue checking.
                    if (matchanymulti)
                    {
                        j++;
                        continue;
                    }
                    return false;
                }
                j++;
                matched = true;
                //Consumse our mask.
                multicharmask = null;
                break;
            }
            //We are in an multiple any-character mask, proceed to the next character.
            if (matchanymulti)
            {
                j++;
                continue;
            }
            break;
        }
        //We've found a match - proceed.
        if (matched)
        {
            matchanymulti = false;
            continue;
        }

        //If no match our mask fails
        return false;
    }
    //Some characters are left - our mask check fails.
    if (j < s.Length)
        return false;
    //We've processed everything - this is a match.
    return true;
} 
Robin GM
  • 185
  • 1
  • 8