LINQ to Entitiesの大文字と小文字を区別する比較


115

これは、LINQ to Entitiesでの大文字と小文字を区別する比較ではありません。

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

LINQ to Entitiesで大文字と小文字を区別して比較するにはどうすればよいですか?


@ロニー:それでよろしいですか?大文字と小文字を区別しない比較を意味しますか?
Michael Petrotta

14
確かに。いいえ、そうではありません。
ロニーオーバーバイ

12
いいえ、SQL Server 2008 R2でEF 4.0を実行している私のコンピューターでは、上記は大文字と小文字を区別しません。EFではデフォルトで大文字と小文字が区別されると多くの場所で言われていますが、これは私が経験したものではありません。
tster

3
それは基礎となるデータベースに依存しませんか?
codymanix

1
@codymanix:いい質問ですね!LinqからEFはDBクエリのラムダ式を変換しますか?答えがわかりません。
Tergiver

回答:


163

これは、最終的にLambda式をSQLステートメントに変換するLINQ To Entitiesを使用しているためです。つまり、大文字と小文字の区別は、デフォルトでSQL_Latin1_General_CP1_CI_ASが設定されているSQL Serverに依存しています照合順序があり、大文字と小文字は区別されません。

ObjectQuery.ToTraceStringを使用して、実際にSQL Serverに送信された生成されたSQLクエリを確認すると、謎が明らかになります。

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

あなたが作成するとエンティティへのLINQクエリを、エンティティへのLINQは LINQの式ツリーにクエリと変換し、処理を開始するためにLINQパーサを利用しています。次に、LINQ式ツリーがObject Services APIに渡されます。これにより、式ツリーがコマンドツリーに変換されます。次に、ストアプロバイダー(SqlClientなど)に送信されます。ストアプロバイダーは、コマンドツリーをネイティブのデータベースコマンドテキストに変換します。クエリはデータストアで実行され、結果はオブジェクトサービスによってエンティティオブジェクトにマテリアライズされます。大文字と小文字の区別を考慮に入れるためのロジックは挿入されていません。そのため、述語をどのような場合でも、その列のSQL Server Collat​​esを変更しない限り、SQL Serverは常に同じものとして扱います。

サーバー側のソリューション:

したがって、最善の解決策は、ThingiesテーブルのName列の照合順序をCOLLATE Latin1_General_CS_ASに変更することです。これは、SQL Serverで次のように実行して大文字と小文字を区別します。

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

SQL Server Collat​​esの詳細については、SQL SERVER Collat​​eの大文字と小文字を区別するSQLクエリ検索をご覧ください。

クライアント側のソリューション:

クライアント側で適用できる唯一のソリューションは、LINQ to Objectsを使用して、非常に洗練されていないように見える別の比較を行うことです。

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

私はEntity Frameworkを使用してデータベーススキーマを生成しているので、私の呼び出しコードを使用したソリューションが最適です。結果が戻ってきたらチェックするつもりだと思います。ありがとう。
ロニーオーバーバイ

問題ない。はい、それは正しいです。クライアント側のソリューションで回答を更新しましたが、あまりエレガントではないので、データストアソリューションを使用することをお勧めします。
Morteza Manavi

18
@eglasiusこれは完全に正しいわけではありません。すべてのデータをフェッチするのではなく、大文字と小文字を区別せずに一致するデータのみをフェッチし、その後、クライアントの大文字と小文字を区別して再度フィルタリングします。もちろん、大文字と小文字を区別せずに一致する何千ものエントリがある場合、そのうちの1つだけが大文字と小文字を区別する正しいものであると、オーバーヘッドが大きくなります。しかし、私は現実がそのようなシナリオを示すとは思わない... :)
Achim

1
@MassoodKhaariあなたが投稿したそのソリューションでは、比較の両側で大文字と小文字を区別しないため、大文字と小文字が区別されません。OPでは、大文字と小文字を区別する比較が必要です。
Jonny、2014

1
「したがって、最善の解決策は、ThingiesテーブルのName列の照合順序をCOLLATE Latin1_General_CS_ASに変更することです。」-これが最善であるとは思いません。ほとんどの場合、大文字と小文字を区別しないLIKEフィルター(.Contains())が必要ですが、場合によっては大文字と小文字を区別する必要があります。私はあなたの「クライアント側ソリューション」を試してみます-私のユースケースでははるかにエレガントです(それが何をするかを理解するのは良いことですが、それをすべて持つことはできません:))。
驚異的なJan

11

EF6 + Code-firstの[CaseSensitive]アノテーションを追加できます

このクラスを追加

[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());
    }
}

DbContextを変更し、追加します

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

それから

Add-Migration CaseSensitive

データベースを更新する

記事https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/に基づき、いくつかのバグを修正しました


11

WHERESQL Serverの条件は、デフォルトでは大文字と小文字を区別しません。列のデフォルトの照合順序(SQL_Latin1_General_CP1_CI_AS)をに変更して、大文字と小文字を区別しますSQL_Latin1_General_CP1_CS_AS

これを行う脆弱な方法は、コードを使用することです。新しい移行ファイルを追加し、Upメソッド内にこれを追加します。

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

だが

新しいEF6機能を使用して、「CaseSensitive」と呼ばれるカスタムアノテーションを作成し、次のようにプロパティを装飾できます。

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

このブログ投稿では、その方法を説明しています。


その記事にはバグがあります
RouR 2015

3

@Morteza Manaviの答えが問題を解決します。それでも、クライアント側のソリューションの場合、エレガントな方法は次のようになります(ダブルチェックを追加します)。

var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
    .FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;

-4

私はモルテザの答えが好きで、通常はサーバー側で修正することを好みます。クライアント側については、私は通常使用します:

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

基本的に、まず必要な基準を持つユーザーがいるかどうかを確認し、次にパスワードが同じかどうかを確認します。少し時間がかかりますが、関連する基準がたくさんあると読みやすいと思います。


2
この回答は、パスワードをデータベースにプレーンテキストとして保存していることを意味します。これは、セキュリティ上の大きな脆弱性です。
Jason Coyne、

2
@JasonCoyne彼が比較しているパスワードはすでにハッシュされている可能性があります
Peter Morris

-4

どちらもStringComparison.IgnoreCase私のために働いた。しかし、これはしました:

context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));

2
これは頼まれた質問、ある、と役に立たないHow can I achieve case sensitive comparison
レッグ編集

-4

string.Equalsを使用

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

また、nullについて心配する必要はなく、必要な情報のみを取得できます。

大文字と小文字を区別しない場合は、StringComparision.CurrentCultureIgnoreCaseを使用します。

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

Equals()はSQLに変換できません...また、インスタンスメソッドを使用しようとすると、StringComparisonは無視されます。
LMK 2016

この解決策を試しましたか?私はEFでうまく機能するように私の最後にこれを試しました。
Darshan Joshi

-6

EF4については不明ですが、EF5はこれをサポートしています。

Thingies
    .First(t => t.Name.Equals(
        "ThingamaBob",
        System.StringComparison.InvariantCultureIgnoreCase)

生成されるsqlに興味があります。
ロニーオーバーバイ2014年

私はこれをEF5でチェックしましたが、SQLでWHERE ... = ...を生成するだけです。したがって、これはSQLサーバー側の照合設定に依存します。
アヒム

DBで大文字と小文字を区別する照合を使用しても、これまたは他のStringComparison列挙型のいずれかで違いを得ることができませんでした。私はものの、問題は(DB-最初の)EDMXファイルのどこかにいると思うために働くべきでこういうことを示唆して十分な人々を見てきましたstackoverflow.com/questions/841226/...
drzaus
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.