130

This isn't a case-sensitive comparison in LINQ to Entities:

Thingies.First(t => t.Name == "ThingamaBob");

How can I achieve case sensitive comparison with LINQ to Entities?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ronnie Overby
  • 45,287
  • 73
  • 267
  • 346
  • @Ronnie: are you sure about that? Do you mean case **insensitive** comparison? – Michael Petrotta Oct 01 '10 at 21:08
  • 14
    Absolutely sure. No I don't mean that. – Ronnie Overby Oct 01 '10 at 21:09
  • It's already case-sensitive, that's why we believe that you're confusing "case-sensitive" with "case-insensitive". – Tergiver Oct 01 '10 at 21:31
  • 14
    No, on my computer running EF 4.0 w/ SQL Server 2008 R2, the above is case insensitive. I know lots of places say that EF is default case sensitive, but that's not what I have experienced. – tster Oct 01 '10 at 21:34
  • EF should have nothing to do with it. What you have is a method (in lambda form) that compares two System.String objects with the == operator. This is, and had better always be a case-sensitive comparison. Something else is going on in your case. – Tergiver Oct 02 '10 at 00:43
  • 3
    Won't that depend on the underlying database? – codymanix Oct 04 '10 at 14:43
  • 1
    @codymanix: That's a good question! Does Linq to EF translate the lambda expression for a DB query? I don't know the answer. – Tergiver Oct 04 '10 at 16:00
  • Have a look at the last answer on [This](http://forums.asp.net/t/1346775.aspx) page. It applies to L2Sql but i believe it will also work in L2E although I have not tried it yet. –  Oct 04 '10 at 14:40
  • @MassoodKhaari -- No that's a horrible suggestion. There are plenty of cultures where that doesn't work; it would only work for, e.g. "en-US". – rory.ap Mar 08 '19 at 19:48
  • Also @RonnieOverby I think everyone is confused about your wording. Saying "This isn't a case-sensitive comparison in LINQ to Entities" also led me to believe at first that you actually meant the opposite because I interpreted it to mean that "This isn't a case-sensitive comparison in C# (i.e. L2E)" and not "This doesn't result in a case sensitive comparison when translated into a query and run against the database." – rory.ap Mar 08 '19 at 19:51
  • @rory.ap You're right. I'd misinterpreted the question. I've deleted my comment now. – Masood Khaari Mar 09 '19 at 07:57

8 Answers8

181

That's because you are using LINQ To Entities which is ultimately convert your Lambda expressions into SQL statements. That means the case sensitivity is at the mercy of your SQL Server which by default has SQL_Latin1_General_CP1_CI_AS Collation and that is NOT case sensitive.

Using ObjectQuery.ToTraceString to see the generated SQL query that has been actually submitted to SQL Server reveals the mystery:

string sqlQuery = ((ObjectQuery)context.Thingies
        .Where(t => t.Name == "ThingamaBob")).ToTraceString();

When you create a LINQ to Entities query, LINQ to Entities leverages the LINQ parser to begin processing the query and converts it into a LINQ expression tree. The LINQ expression tree is then passed to Object Services API, which converts the expression tree to a command tree. It is then sent to the store provider (e.g. SqlClient), which convert the command tree into the native database command text. Query get executed on the data store and the results are Materialized into Entity Objects by Object Services. No logic has been put in between to take case sensitivity into account. So no matter what case you put in your predicate, it will always treat as the same by your SQL Server unless you change your SQL Server Collates for that column.

Server side solution:

Therefore, the best solution would be to change the collation of the Name column in the Thingies table to COLLATE Latin1_General_CS_AS which is case sensitive by running this on your SQL Server:

ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS

For more information on the SQL Server Collates, take a a look at SQL SERVER Collate Case Sensitive SQL Query Search

Client-side solution:

The only solution that you can apply on client side is to use LINQ to Objects to do yet another comparison which doesn't seem to be very elegant:

Thingies.Where(t => t.Name == "ThingamaBob")
        .AsEnumerable()
        .First(t => t.Name == "ThingamaBob");
Community
  • 1
  • 1
Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83
  • I am generating the database schema with Entity Framework, so a solution using my calling code would be best. I guess I will do a check after the results have come back. Thanks. – Ronnie Overby Oct 04 '10 at 12:35
  • No problem. Yes, that is correct and I've updated my answer with a client side solution, however it isn't very elegant and I still recommend to use the data store solution. – Morteza Manavi Oct 04 '10 at 14:29
  • +1 good answer. For anyone stopping by, the client side solution is only for tiny sets of data (it's retrieving all the data & then filtering at the client side). – eglasius Dec 26 '11 at 21:34
  • 24
    @eglasius This is not completely true: It does not fetch ALL data, it fetches only the data that matches case insensitively, and after that it gets filtered again on the client case sensitively. Of course, if you happen to have thousands of entries that match case insensitive, but only one of them is the correct one case sensitive, then it's a lot of overhead. But I don't think that reality will present such scenarios... :) – Achim Jun 05 '14 at 10:14
  • 1
    @MassoodKhaari That solution you posted would make the it Case Insensitive because you're lower casing both sides of the comparison. The OP needs a case sensitive comparison. – Jonny Sep 08 '14 at 11:14
  • @Jonny Oh, you're right. Really sorry about my naive mistake. Wondering why I haven't noticed. – Masood Khaari Sep 08 '14 at 12:14
  • I *LOVE* Linq but this one bit me for a day or two now. Finally narrowed it down to this. The elegance LINQ embodies hides the nitty gritty which sometimes bites ya! But at least there is another "client-sideish" solution for Entity Framework using Migrations. It essentially says do a migration that executes the SQL in this answer (and it worked for me). https://www.talksharp.com/case-sensitive-fields-in-entity-framework-code-first. Don't forget to add the inverse in the .Down() method! –  Jul 22 '15 at 19:08
  • I don't have `ToTraceString()` extension method. It's not recognized. ? – A-Sharabiani Sep 10 '15 at 16:02
  • you could send a hash of your string to match with a hash of the DB string, chances of different strings to match hashes are very small – Sergiu Mindras Mar 20 '17 at 08:58
  • 1
    "Therefore, the best solution would be to change the collation of the Name column in the Thingies table to COLLATE Latin1_General_CS_AS" - I don't think that this is the best. Most of the time I need case insensitive LIKE filter (.Contains()) but sometimes it should be case sensitive. I'll try your "Client-side solution" - it's much more elegant for my use case I think ( would be nice to understand what it does but you can't have it all :) ). – The incredible Jan May 10 '17 at 08:57
  • Server side solution was by far the easiest and ensured that any sprocs also benefit from the change. Thank you! – Toad Jul 29 '19 at 09:48
11

WHERE conditions in SQL Server are case insensitive by default. Make it case sensitive by changing the column's default collations (SQL_Latin1_General_CP1_CI_AS) to SQL_Latin1_General_CP1_CS_AS.

The fragile way to do this is with code. Add a new migration file and then add this inside the Up method:

public override void Up()
{
   Sql("ALTER TABLE Thingies ALTER COLUMN Name VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
}

But

You can create custom annotation called "CaseSensitive" using the new EF6 features and you can decorate your properties like this:

[CaseSensitive]
public string Name { get; set; }

This blog post explains how to do that.

Laurel
  • 5,965
  • 14
  • 31
  • 57
Milina Udara
  • 597
  • 1
  • 9
  • 24
11

You can add [CaseSensitive] annotation for EF6+ Code-first

Add this classes

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class CaseSensitiveAttribute : Attribute
{
    public CaseSensitiveAttribute()
    {
        IsEnabled = true;
    }
    public bool IsEnabled { get; set; }
}

public class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AlterColumnOperation alterColumnOperation)
    {
        base.Generate(alterColumnOperation);
        AnnotationValues values;
        if (alterColumnOperation.Column.Annotations.TryGetValue("CaseSensitive", out values))
        {
            if (values.NewValue != null && values.NewValue.ToString() == "True")
            {
                using (var writer = Writer())
                {
                    //if (System.Diagnostics.Debugger.IsAttached == false) System.Diagnostics.Debugger.Launch();

                    // https://github.com/mono/entityframework/blob/master/src/EntityFramework.SqlServer/SqlServerMigrationSqlGenerator.cs
                    var columnSQL = BuildColumnType(alterColumnOperation.Column); //[nvarchar](100)
                    writer.WriteLine(
                        "ALTER TABLE {0} ALTER COLUMN {1} {2} COLLATE SQL_Latin1_General_CP1_CS_AS {3}",
                        alterColumnOperation.Table,
                        alterColumnOperation.Column.Name,
                        columnSQL,
                        alterColumnOperation.Column.IsNullable.HasValue == false || alterColumnOperation.Column.IsNullable.Value == true ? " NULL" : "NOT NULL" //todo not tested for DefaultValue
                        );
                    Statement(writer);
                }
            }
        }
    }
}

public class CustomApplicationDbConfiguration : DbConfiguration
{
    public CustomApplicationDbConfiguration()
    {
        SetMigrationSqlGenerator(
            SqlProviderServices.ProviderInvariantName,
            () => new CustomSqlServerMigrationSqlGenerator());
    }
}

Modify your DbContext, add

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<CaseSensitiveAttribute, bool>(
                "CaseSensitive",
                (property, attributes) => attributes.Single().IsEnabled));
        base.OnModelCreating(modelBuilder);
    }

Then do

Add-Migration CaseSensitive

Update-Database

based on article https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ with some bug fix

RouR
  • 6,136
  • 3
  • 34
  • 25
5

The answer given by @Morteza Manavi solves the problem. Still, for a client-side solution, an elegant way would be the following (adding a double check).

var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
    .FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
Beatles1692
  • 5,214
  • 34
  • 65
Swarup Rajbhandari
  • 466
  • 1
  • 6
  • 14
  • 1
    It’s not so elegant because what if the single record returned is not an exact case sensitive match? Do we go back to the database? There may be a case-sensitive match we did not retrieve. – El Ronnoco Feb 07 '21 at 07:49
  • This is quite the hack, but in some cases it could actually be a easy workaround (still a hack tho) that does not require changing database collation for a single case-sensitive query. Thanks for the idea :) – Hannes Sachsenhofer Mar 21 '22 at 10:07
-4

I liked Morteza's answer, and would normally prefer to fix on server side. For client-side I normally use:

Dim bLogin As Boolean = False

    Dim oUser As User = (From c In db.Users Where c.Username = UserName AndAlso c.Password = Password Select c).SingleOrDefault()
    If oUser IsNot Nothing Then
        If oUser.Password = Password Then
            bLogin = True
        End If
    End If

Basically, first checking if there is a user with required criteria, then check if the password is the same. A little bit long-winded, but I feel it is easier to read when there may be a whole bunch of criteria involved.

Rune Borgen
  • 369
  • 1
  • 4
  • 11
-4

Use string.Equals

Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCulture);

Also, you don't have to worry about null and get back only the information you want.

Use StringComparision.CurrentCultureIgnoreCase for Case Insensitive.

Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCultureIgnoreCase);
-5

Not sure about EF4, but EF5 supports this:

Thingies
    .First(t => t.Name.Equals(
        "ThingamaBob",
        System.StringComparison.InvariantCultureIgnoreCase)
bloparod
  • 1,716
  • 1
  • 14
  • 17
  • Curious what sql that generates. – Ronnie Overby Jan 22 '14 at 12:47
  • I checked this with EF5, it simply generated a WHERE ... = ... in SQL. So again, this is dependent on the collation settings on the SQL server side. – Achim Jun 05 '14 at 10:15
  • Even with a case-sensitive collation in the DB I couldn't get this or any of the other `StringComparison` enums to make a difference. I've seen enough people suggesting this kind of thing should work to think the problem is somewhere in the EDMX file (db-first), although http://stackoverflow.com/questions/841226/case-insensitive-string-compare-in-linq-to-sql – drzaus Feb 26 '16 at 14:11
-5

Neither of the StringComparison.IgnoreCase worked for me. But this did:

context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));
arcyqwerty
  • 10,325
  • 4
  • 47
  • 84
saquib adil
  • 146
  • 10
  • 4
    This wouldn't help with the question that was asked, which is, `How can I achieve case sensitive comparison` – Reg Edit Apr 30 '15 at 17:06