web-dev-qa-db-ja.com

EF 6.1一意のNULL可能インデックス

EF 6.1では、Code Firstを使用して、エンティティの属性を使用するか、以下のようにFluent APIを使用してインデックスを作成できます。

 Property(x => x.PropertyName)
                .IsOptional()
                .HasMaxLength(450)
                .HasColumnAnnotation("Index",
                    new IndexAnnotation(new IndexAttribute("IX_IndexName") {IsUnique = true,  }));

足場を言う方法はありますかWHERE PropertyName IS NOT NULL SQL Serverでネイティブに行うのと同じ方法で(参照: https://stackoverflow.com/a/767702/52026 )?

45
MrEdmundo

このWHERE句を使用するようにEFに指示する方法が見つかりませんでしたが、いくつかの回避策があります。それがあなたのケースに収まるかどうかを確認してください。

  1. Entity Frameworkをインストールし、DbContext、エンティティ、app.configのconn文字列などを定義します。
  2. 移行を有効にする-パッケージマネージャーコンソールで実行する-EnableMigration
  3. DbMigrationの作成-パッケージマネージャーコンソールで実行 'Add-Migration MigrationName'
  4. Ovverided Upメソッドで作成されたDbMigrationクラスで、SQLを実行して、一意のnull許容インデックスを作成します。

コード:

// Add unique nullable index 
string indexName = "IX_UQ_UniqueColumn";
string tableName = "dbo.ExampleClasses";
string columnName = "UniqueColumn";

Sql(string.Format(@"
    CREATE UNIQUE NONCLUSTERED INDEX {0}
    ON {1}({2}) 
    WHERE {2} IS NOT NULL;",
    indexName, tableName, columnName));

注:ダウングレードも忘れずに作成してください。 Ovveride Downメソッドを使用し、内部でDropIndexメソッドを使用します。

DropIndex(tableName, indexName);

また、一意のインデックス制約と競合する可能性のあるデータがデータベースにすでにある場合は、追加のコードが必要になる場合があります。

注:ここではCreateIndexメソッドを使用できますが、それを使用して正しいインデックスを作成することができませんでした。 EFは私のanonymousArgumentsを無視するか、間違って記述します。自分で試して、結果をここに書いてください。構文は次のとおりです。

CreateIndex(
    table: "dbo.ExampleClasses",
    columns: new string[] { "UniqueColumn" },
    unique: true,
    name: "IX_UniqueColumn",
    clustered: false,
    anonymousArguments: new
    {
        Include = new string[] { "UniqueColumn" },
        Where = "UniqueColumn IS NOT NULL"
    });

5一意の列と他の等しい値にnull値を持つ2つのエトリーを追加してみます。

これが私のデモコードです- Pastebin

51
Viktor Bahtev

EF Coreでは、Fluent APIのHasFilterメソッドを使用して、カスタムSQLを移行に追加することなく、探しているものを実現できます。

builder.Entity<Table>()
    .HasIndex(x => x.PropertyName)
    .HasName("IX_IndexName")
    .HasFilter("PropertyName IS NOT NULL");

これにより、次のような移行が生成されます。

migrationBuilder.CreateIndex(
    name: "IX_IndexName",
    table: "Table",
    columns: new[] { "PropertyName" },
    filter: "PropertyName IS NOT NULL");
9
Sam

いいえ、あなたはそれを行うことができません自然に

しかし、以下を可能にするカスタムSQLジェネレーターを作成しました。

  1. インデックスの列を並べ替えるASCまたはDESC
  2. WHEREキーワードの使用を有効にする

これを使用できるようにするには、インデックス名のみを調整する必要があります。名前は:で3つの部分に分かれています。パーツは次のとおりです。

  1. インデックス名
  2. 並べ替え
  3. WHERE句

2つの列にインデックスがある場合、Column1をソートする必要がありますASCおよびColumn2DESC、およびwhere句が必要です、インデックス名前は次のようになります:

var uniqueName = "UN_MyIndex:ASC,DESC:Column1 IS NOT NULL";

そして、あなたは単にこのようにそれを使います:

Property(t => t.Column1)
            .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute(uniqueName) { IsUnique = true, Order = 1 }));

Property(t => t.Column2)
            .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute(uniqueName) { IsUnique = true, Order = 2 }));

次に、Configuration.csファイルで、コンストラクタに次の行を追加します。

SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerMigrationSqlGenerator());

最後に、次のようにCustomSqlServerMigrationSqlGenerator.csファイルを作成します: ここにコード

0
Maxime

Viktorの答えに基づいて、このコードを自動的に作成するソリューションを思いつきました。

最終的な移行ファイルは、CreateIndexメソッドではなく、CreateIndexNullableという名前のメソッドを使用する必要があります。 DbMigrationExを拡張するDbMigrationで作成したこのメソッド

protected void CreateIndexNullable(string table, string column, string name)
{
    Sql($@"CREATE UNIQUE NONCLUSTERED INDEX [{name}] ON {table}([{column}] ASC) WHERE([{column}] IS NOT NULL);");
}

移行クラスコードを変更する方法

設定したMigrationフォルダに作成されるConfigurationクラスに

CodeGenerator = new CSharpMigrationCodeGeneratorIndexNullable();

私のCSharpMigrationCodeGeneratorIndexNullableクラスはCSharpMigrationCodeGeneratorを拡張します。正確なクラスコンテンツを表示するつもりはないので、アイデアを提示します。 CSharpMigrationCodeGeneratorコンテンツに基づいて、いくつかのメソッドをオーバーライドしました。 Entity Frameworkプロジェクトは https://github.com/aspnet/EntityFramework6 にあります。

移行クラスをDbMigrationExに変更するには、メソッドを使用しました

Generate(IEnumerable<MigrationOperation> operations, string @namespace, string className)

変更が必要なのは

WriteClassStart(
    @namespace, className, writer, "DbMigration", designer: false,
    namespaces: GetNamespaces(operations));

移行方法をCreateIndexNullableに変更するには、メソッドを使用しました

Generate(CreateIndexOperation createIndexOperation, IndentedTextWriter writer)

行を変更する必要があります

writer.Write("CreateIndex(");

また

WriteIndexParameters(createIndexOperation, writer);

writer.Write(", ");
writer.Write(Quote(createIndexOperation.Name));

しかし、インデックスがnull可能でなければならないかどうかを知る方法は?

createIndexOperationパラメータにはインデックス情報が含まれています。 CreateIndexOperationの作成を変更することはできませんでしたが、そのTableName、およびColumnsプロパティは、エンティティクラスのフィールドに到達し、拡張可能なIndex属性を取得するのに十分でした。

0
gangus