How to do SQL Like % in Linq?
.NetLinqTsqlLinq to-EntitiesSql Like.Net Problem Overview
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?
.Net Solutions
Solution 1 - .Net
.Where(oh => oh.Hierarchy.Contains("/12/"))
You can also use .StartsWith()
or .EndsWith()
.
Solution 2 - .Net
Use this:
from c in dc.Organization
where SqlMethods.Like(c.Hierarchy, "%/12/%")
select *;
Solution 3 - .Net
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.
Solution 4 - .Net
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.
Solution 5 - .Net
Well indexOf works for me too
var result = from c in SampleList
where c.LongName.IndexOf(SearchQuery) >= 0
select c;
Solution 6 - .Net
.NET core now has EF.Functions.Like
var isMatch = EF.Functions.Like(stringThatMightMatch, pattern);
Solution 7 - .Net
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;
}
Solution 8 - .Net
In case you are not matching numeric strings, always good to have common case:
.Where(oh => oh.Hierarchy.ToUpper().Contains(mySearchString.ToUpper()))
Solution 9 - .Net
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.
Solution 10 - .Net
System.Data.Linq.SqlClient.SqlMethods.Like("mystring", "%string")
Solution 11 - .Net
Try this, this works fine for me
from record in context.Organization where record.Hierarchy.Contains(12) select record;
Solution 12 - .Net
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;
}
}
Solution 13 - .Net
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});
Solution 14 - .Net
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.
Solution 15 - .Net
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;
}