204

This code is case sensitive, how to make it case insensitive?

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM.Where(fi => fi.DESCRIPTION.Contains(description));
}
Jeaf Gilbert
  • 11,495
  • 19
  • 78
  • 105
  • Sjoerd's answers is correct but... I want to get search results for names with a turkish İ (for example) when writing an i and vice versa. In this case ToLower seems to be the correct way to go. Please correct me if i'm wrong. About turkish İ: https://en.wikipedia.org/wiki/Dotted_and_dotless_I – He Nrik Sep 13 '18 at 21:01
  • @HeNrik - As discussed in Turkey Test link in JYelton's comment under accepted answer, when run with Turkish culture, those two i's will be different - so you won't find names with the other i. You want ToLowerInvariant. See discussion under various answers [here](https://stackoverflow.com/questions/6225808/string-tolower-and-string-tolowerinvariant). – ToolmakerSteve Sep 03 '19 at 20:37
  • 1
    this is an old question, but it is worth to note that in the current version EF core 2.0 ToLower() works as follows person.Where(p => p.Name.ToLower().Contains(myParam.Name.ToLower())); I am using this in a Linq query against a Postgres DB. I do not have case insensitivty on the column collation in the DB and I checked that without ToLower() the match is clearly case sensitive. – shelbypereira Sep 26 '19 at 05:46
  • 1
    I'm surprised to see that most answers seem to think that this is LINQ to objects. Only a few are aware of the database collation as the determining factor. `IQueryable` and `ObjectContext` should be a clear indication that this is about a LINQ-to-entities query. – Gert Arnold Oct 18 '20 at 08:33

11 Answers11

264
fi => fi.DESCRIPTION.ToLower().Contains(description.ToLower())
IluTov
  • 6,807
  • 6
  • 41
  • 103
Nealv
  • 6,856
  • 8
  • 58
  • 89
  • 1
    there might be a more elegant solution, but this is logical and works anyhow. I will look for the elegant solution later – Nealv Jul 29 '10 at 09:20
  • 61
    As [Jon Skeet](http://stackoverflow.com/users/22656/jon-skeet) commented on a [related question](http://stackoverflow.com/a/444813/161052), this method won't pass the [Turkey Test](http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html). – JYelton Jun 29 '12 at 20:57
  • 5
    No, but databases work off of character sets and collation. If you're trying to push off work to the database, you have to make some assumptions about character set and collation, right? – Christopher Stevenson Mar 28 '13 at 12:17
  • 82
    Contains should be using `IEqualityComparer` attribute to handle how the comparison will work. Use ToLower and ToUpper to check equality is a bad idea. Try: `.Contains(description, StringComparer.CurrentCultureIgnoreCase)` for example – Dorival Apr 10 '14 at 13:57
  • 3
    The comment by @Dorival should be the answer. – DarrellNorton Apr 28 '14 at 03:24
  • 22
    The comment from @Dorival doesnt work, as it gives this errormessage: `Error 1 'string' does not contain a definition for 'Contains' and the best extension method overload 'System.Linq.ParallelEnumerable.Contains(System.Linq.ParallelQuery, TSource, System.Collections.Generic.IEqualityComparer)' has some invalid arguments` – eMi Feb 17 '15 at 12:49
  • 2
    There are many ways to do that, the use of Equality is always recommended. Another way, perhaps even faster is to use the `IndexOf` method as: `.Where(fi => fi.DESCRIPTION.IndexOf(description, StringComparison.OrdinalIgnoreCase) >= 0));`. A solution that Jeff Mercado suggested as an answer in this page. – Dorival Feb 25 '15 at 16:45
  • 7
    `Contains` with `StringComparer` doesn't receive string as parameter, so it will be build-error. `IndexOf` on `Queryable` probably can't be translated into SQL. Personally I found this answer to be totally valid as we speak about LINQ to database. – Thariq Nugrohotomo Apr 10 '15 at 06:56
  • 4
    If you are going to use fi => fi.DESCRIPTION.ToLower().Contains(description.ToLower()) you should first store the lowercase value in a variable (var desc = description.ToLower()) so you don't repeat the ToLower() for each item in your recordset. – Difinity Nov 21 '15 at 17:19
  • A very elegant solution. Usually, case-sensitive issue only appears on poorly designed programs, and this is a good patch for such systems. Personally, I should have written a code to format every string before it goes to the database, but didn't bother with that. So I need this. – Spero Jun 16 '19 at 08:03
137

If the LINQ query is executed in database context, a call to Contains() is mapped to the LIKE operator:

.Where(a => a.Field.Contains("hello")) becomes Field LIKE '%hello%'. The LIKE operator is case insensitive by default, but that can be changed by changing the collation of the column.

If the LINQ query is executed in .NET context, you can use IndexOf(), but that method is not supported in LINQ to SQL.

LINQ to SQL does not support methods that take a CultureInfo as parameter, probably because it can not guarantee that the SQL server handles cultures the same as .NET. This is not completely true, because it does support StartsWith(string, StringComparison).

However, it does not seem to support a method which evaluates to LIKE in LINQ to SQL, and to a case insensitive comparison in .NET, making it impossible to do case insensitive Contains() in a consistent way.

Sjoerd
  • 74,049
  • 16
  • 131
  • 175
93

Assuming we're working with strings here, here's another "elegant" solution using IndexOf().

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
        .Where(fi => fi.DESCRIPTION
                       .IndexOf(description, StringComparison.OrdinalIgnoreCase) != -1);
}
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
14

The accepted answer here does not mention a fact that if you have a null string ToLower() will throw an exception. The safer way would be to do:

fi => (fi.DESCRIPTION ?? string.Empty).ToLower().Contains((description ?? string.Empty).ToLower())
Shiva
  • 20,575
  • 14
  • 82
  • 112
Marko
  • 12,543
  • 10
  • 48
  • 58
  • You can't gen an exception on a query translated to SQL – Alex Zhukovskiy Jul 24 '17 at 10:49
  • @AlexZhukovskiy How is that even relevant to this problem? If fi.DESCRIPTION is null or description is null you're getting a C# null reference exception. It doesn't matter what the LINQ query converts to on the SQL side. Here's the proof: https://dotnetfiddle.net/5pZ1dY – Marko Oct 25 '17 at 20:45
  • Because this query will fail translation to SQL because it doesn't support null coaleshing operator. And you probably quering database instead of loading out all the entries to use null coaleshing on client side. So if you use it - it's ok on client side but fail on DB, otherwise you're ok with DB and you don't care about nullref on client side because it won't happen becasue C# doesn't execute this query and doesn't actually read null objects. – Alex Zhukovskiy Oct 26 '17 at 10:39
  • This answer helped me solve a problem I was getting on LINQ to Entities where I was doing .IndexOf and .Contains on an IEnumerable where the string value coming from the database was null. I wasn't getting the error until the result was enumerated and then I got an error message that "Object reference not set to an instance of an object." I couldn't figure out why it was occurring until I saw this post. Thanks! – randyh22 Dec 20 '18 at 19:53
8

Using C# 6.0 (which allows expression bodied functions and null propagation), for LINQ to Objects, it can be done in a single line like this (also checking for null):

public static bool ContainsInsensitive(this string str, string value) => str?.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
5

IndexOf works best in this case

return this
   .ObjectContext
   .FACILITY_ITEM
   .Where(fi => fi.DESCRIPTION.IndexOf(description, StringComparison.OrdinalIgnoreCase)>=0);
Menelaos Vergis
  • 3,715
  • 5
  • 30
  • 46
5

Honestly, this doesn't need to be difficult. It may seem that on the onset, but it's not. Here's a simple linq query in C# that does exactly as requested.

In my example, I'm working against a list of persons that have one property called FirstName.

var results = ClientsRepository().Where(c => c.FirstName.ToLower().Contains(searchText.ToLower())).ToList();

This will search the database on lower case search but return full case results.

Azametzin
  • 5,223
  • 12
  • 28
  • 46
Frank Thomas
  • 350
  • 5
  • 12
  • as said in another answer, this fails if `searchText` is null or if any of the `c.FirstName` are null. – Ross Presser Sep 12 '22 at 15:17
  • 1
    You will just have to perform extra handling on this. First if the searchText is null, then you wrap this in an if statement. Second, perform a null check on c.FirstName, such as c=> c.FirstName != null && c.FirstName.ToLower().... The first part of the and condition should stop it checking the second if the first name is null. – Frank Thomas Sep 15 '22 at 19:24
4

You can use string.Compare

    lst.Where(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0);

if you just want to check contains then use "Any"

  lst.Any(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0)
Code First
  • 169
  • 1
  • 2
  • 8
  • This doesn’t answer the question. The OP is asking about ‘Contains’ _within a string_ (i.e., one string contains another), not whether a collection of strings contains a single string. – andrewf Apr 10 '20 at 12:28
2
public static bool Contains(this string input, string findMe, StringComparison comparisonType)
{
    return String.IsNullOrWhiteSpace(input) ? false : input.IndexOf(findMe, comparisonType) > -1;
}
E Rolnicki
  • 1,677
  • 2
  • 17
  • 26
1

StringComparison.InvariantCultureIgnoreCase just do the job for me:

.Where(fi => fi.DESCRIPTION.Contains(description, StringComparison.InvariantCultureIgnoreCase));
Yanga
  • 2,885
  • 1
  • 29
  • 32
-1

Use String.Equals Method

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
           .Where(fi => fi.DESCRIPTION
           .Equals(description, StringComparison.OrdinalIgnoreCase));
}
Gericke
  • 2,109
  • 9
  • 42
  • 71