A few days ago we published a small post explaining how to perform a case insensitive Contains query using Entity Framework Core, since the Contains() method changed its behaviour from being case-insensitive (Entity Framework and Entity Framework Core 1.0) to case-sensitive (EF Core 1.1 and onwards). In this post we'll expant that answer to see how the fix that we've suggested there can be implemented when we build our LINQ-based queries using System.Linq.Dynamic.
System.Linq.Dynamic: how it works
For those who don't know what System.Linq.Dynamic actually is, let's just say that we're talking about one of the best NuGet packages you can get while working with Entity Framework, which also has an awesome .NETStandard & .NET Core port called System.Linq.Dynamic.Core that can be used with Entity Framework Core: in a nutshell, it's a library that adds dynamic language functionalities to LINQ using a powerful set of extensions that could be used to turn something like this:
1 |
dresses = DbContext.Dresses.Where(i => i.DressName == "TestName"); |
into this:
1 |
dresses = DbContext.Dresses.Where("DressName == 'TestName'"); |
or (more conveniently) into this:
1 |
dresses = DbContext.Dresses.Where("DressName == @0", colValue); |
or (even more conveniently) into this:
1 |
dresses = DbContext.Dresses.Where("@0 == @1", colName, colValue); |
As we can easily guess, @0 and @1 are placeholders for variables, just like {0} and {1} are for the string.Format method.
The issue
Now that we know what we're talking about, let's come to the main topic of this post. System.Linq.Dynamic is awesome, but its Core counterpart - since it relies upon Entity Framework Core - suffers from the same case sensitivity issue that affects its "big uncle".
More specifically, if we do this:
1 |
this.someQuery = this.someQuery.Where("{0}.Contains(@0, true)", field, strValue); |
The Contains() method will be processed in a case-sensitive way: this means that if the DB column represented by [field] contains the word "Monkey" and strValue has a value of "monk", the method will return False.
The fix
Luckily enough ,there at least two ways to overcome such issue and being able to use the Contains() method in a case-insensitive way:
Method #1: ugly, but works
Here's a viable fix:
1 2 |
this.someQuery = this.someQuery.Where("{0}.ToLower().Contains(@0, true)", field, strValue.ToLower()); |
Ah, the good old "double lowecase" approach: not too bad, isn't it? However, we can do better.
Method #2: Looks better (and also works)
Here's an improved way to solve our problem for good:
1 2 |
this.someQuery = this.someQuery.Where(string.Format("{0}.Contains(@0, \"{1}\")", field, StringComparison.InvariantCultureIgnoreCase.ToString()), strValue); |
As we can see, instead of relying to the "double lowercase" approach, we've exploited the Contains() method overload that accept a StringComparison parameter: not only this works well, but it also allows us to use different StringComparison values for different scenarios, thus allowing to choose between applying casing or not depending on the circumstance.
Such strategy is definitely more versatile that the previous one and can be used to create a general purpose helper method to conditionally handle the casing like the following one:
1 2 3 4 5 6 7 8 9 10 11 |
public static IQueryable<TEntity> WhereContains<TEntity>( this IQueryable<TEntity> query, string field, string value, StringComparison comparisonType = StringComparison.InvariantCultureIgnoreCase) where TEntity : class { return query.Where(string.Format("{0}.Contains(@0, \"{1}\")", field, comparisonType.ToString()), value); } |
That's definitely the way to go.
Conclusion
That's it, at least for now: I hope that this post will help other ASP.NET and ASP.NET Core developers looking for a way to use the Contains() method in a case-insensitive way just like they were used to do in older version of Entity Framework.