これは、LINQ to Entitiesでの大文字と小文字を区別する比較ではありません。
Thingies.First(t => t.Name == "ThingamaBob");
LINQ to Entitiesで大文字と小文字を区別して比較するにはどうすればよいですか?
これは、LINQ to Entitiesでの大文字と小文字を区別する比較ではありません。
Thingies.First(t => t.Name == "ThingamaBob");
LINQ to Entitiesで大文字と小文字を区別して比較するにはどうすればよいですか?
回答:
これは、最終的に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 Collatesを変更しない限り、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 Collatesの詳細については、SQL SERVER Collateの大文字と小文字を区別するSQLクエリ検索をご覧ください。
クライアント側で適用できる唯一のソリューションは、LINQ to Objectsを使用して、非常に洗練されていないように見える別の比較を行うことです。
Thingies.Where(t => t.Name == "ThingamaBob")
.AsEnumerable()
.First(t => t.Name == "ThingamaBob");
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/に基づき、いくつかのバグを修正しました
WHERE
SQL 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; }
このブログ投稿では、その方法を説明しています。
@Morteza Manaviの答えが問題を解決します。それでも、クライアント側のソリューションの場合、エレガントな方法は次のようになります(ダブルチェックを追加します)。
var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
.FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;
私はモルテザの答えが好きで、通常はサーバー側で修正することを好みます。クライアント側については、私は通常使用します:
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
基本的に、まず必要な基準を持つユーザーがいるかどうかを確認し、次にパスワードが同じかどうかを確認します。少し時間がかかりますが、関連する基準がたくさんあると読みやすいと思います。
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);
EF4については不明ですが、EF5はこれをサポートしています。
Thingies
.First(t => t.Name.Equals(
"ThingamaBob",
System.StringComparison.InvariantCultureIgnoreCase)
StringComparison
列挙型のいずれかで違いを得ることができませんでした。私はものの、問題は(DB-最初の)EDMXファイルのどこかにいると思うために働くべきでこういうことを示唆して十分な人々を見てきましたstackoverflow.com/questions/841226/...