web-dev-qa-db-ja.com

Entity Framework 6.1(Coreではない)で、IndexAttributeを使用してクラスター化インデックスを定義するにはどうすればよいですか?

Entity Framework 6.1(code-first)は、IndexAttributeを介してインデックスを追加する可能性を追加しました。この属性は、インデックスをクラスター化するか非クラスター化するかを指定するためのパラメーターを取ります。

同時に、AFAIK、Entity Frameworkでは、すべてのエンティティに主キー(KeyAttributeで注釈が付けられている)が必要であり、その主キーは常にclusteredとして作成されます。 )キー。

したがって、IndexAttributeIsClustered = trueで適用するとすぐにエラーが発生します。これは、キーが原因で、クラスター化インデックスがすでに存在しているためです

では、IndexAttributeを使用して、主キーではないクラスター化インデックスを作成するにはどうすればよいですか? IsClusteredIndexAttributeプロパティはまったく使用できますか?

(もう少しコンテキスト:LINQクエリを介した読み取りにのみ使用されるテーブルをマッピングしています。そのテーブルからエンティティを実際に挿入、更新、または削除する必要はありません。したがって、主キーは必要ありません。理想的には、主キーはないが、読み取り用に最適化された一意ではないクラスター化されたインデックスを持つテーブルが必要です。)

編集(2014-04-11):関連項目 https://entityframework.codeplex.com/workitem/2212 .

18
Fabian Schmied

テーブルには1つのクラスター化インデックスしか存在できず、デフォルトではEntity Framework/SQLServerがそれを主キーに配置します。

では、主キーではないインデックスのIsClustered属性はどのように使用されますか?良い質問! (+1)

このクラス:

public class Blog
{
    [Key()]
    public int Id { get; set; }

    [MaxLength(256)]//Need to limit size of column for clustered indexes
    public string Title { get; set; }

    [Index("IdAndRating", IsClustered = true)]
    public int Rating { get; set; }

}

この移行を生成します:

    public override void Up()
    {
        CreateTable(
            "dbo.Blogs",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Title = c.String(maxLength: 256),
                    Rating = c.Int(nullable: false),
                });
            .PrimaryKey(t => t.Id)
            .Index(t => t.Rating, clustered: true, name: "IdAndRating");
    }

これに移行を変更します。

    public override void Up()
    {
        CreateTable(
            "dbo.Blogs",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Title = c.String(maxLength: 256),
                    Rating = c.Int(nullable: false),
                });

        CreateIndex("dbo.Blogs", 
                    new[] { "Rating", "Title" }, 
                    clustered: true, 
                    name: "IdAndRating");

    }

そして、それは主キーなしで、他の列のクラスター化インデックスを使用してテーブルを作成する必要があります

[〜#〜] edit [〜#〜]データを挿入、更新、または削除する必要がないシナリオでは、本格的なエンティティが必要な場合は、 raw sql querys を使用してクラスにデータを入力できます。 EFはテーブルを自動化しないため、移行に独自のSQLを追加してテーブルを作成する必要がありますが、これは、テーブルとインデックスを必要に応じて作成できることを意味します。

9
Colin

SqlServerMigrationSqlGeneratorから独自のクラスを派生させ、そこでpkの作成を変更できます。

public class NonClusteredPrimaryKeySqlMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(System.Data.Entity.Migrations.Model.AddPrimaryKeyOperation addPrimaryKeyOperation)
    {
        addPrimaryKeyOperation.IsClustered = false;
        base.Generate(addPrimaryKeyOperation);
    }

    protected override void Generate(System.Data.Entity.Migrations.Model.CreateTableOperation createTableOperation)
    {
        createTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(createTableOperation);
    }

    protected override void Generate(System.Data.Entity.Migrations.Model.MoveTableOperation moveTableOperation)
    {
        moveTableOperation.CreateTableOperation.PrimaryKey.IsClustered = false;
        base.Generate(moveTableOperation);
    }

ここに完全な例 https://entityframework.codeplex.com/workitem/216

5
raditch

以下は、私のために働いたラディッチの答えに基づくコードです。これにより、主キーをデフォルトでクラスター化できます。組み込みのefマイグレーションを使用して実際に変更を処理しないため、微調整が必​​要になる場合があります。

public class NonClusteredPrimaryKeySqlMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    public override IEnumerable<System.Data.Entity.Migrations.Sql.MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
    {
        var primaries = migrationOperations.OfType<CreateTableOperation>().Where(x => x.PrimaryKey.IsClustered).Select(x => x.PrimaryKey).ToList();
        var indexes = migrationOperations.OfType<CreateIndexOperation>().Where(x => x.IsClustered).ToList();
        foreach (var index in indexes)
        {
            var primary = primaries.Where(x => x.Table == index.Table).SingleOrDefault();
            if (primary != null)
            {
                primary.IsClustered = false;
            }
        }
        return base.Generate(migrationOperations, providerManifestToken);
    }
}
public class EFCustomConfiguration : DbConfiguration
{
    public EFCustomConfiguration()
    {
        SetMigrationSqlGenerator("System.Data.SqlClient", () => new NonClusteredPrimaryKeySqlMigrationSqlGenerator());
    }
}
2
keith.kauffman

実を言うと、IndexAttributeは完全に冗長であり、専門的な開発には適していません。それらはコア機能を欠いており、ほとんど意味のないものに焦点を合わせています。

どうして?ビルドスクリプトのように柔軟である必要はありません。クラスター化インデックスは1つだけです。次に見逃すのは、フィルター処理されたインデックスです。ほとんどの場合、フィールドの「null以外の一意のインデックス、nullの一意でないインデックス」の形式で、オプションで非常に定期的に使用します。一意のコード(SQL ServerではNULLはSQL生成の別のNULLと等しいため、一意のインデックスには一度に1つのNULLしか含めることができません)。

もし私があなたなら、データベースの生成(および移行)を避け、従来のセットアップ/移行スクリプトのアプローチを使用します。 Thtaは、損失を発生させることなく、より複雑なマルチステップ移行を実行できるものです。 EFは、最も基本的なシナリオ以外は何も処理しません。これらの領域では、それで十分だとは思えません。それは、私もほとんどの場合、変更を非常に慎重に行う大規模なデータベースで作業しているためです。インデックスの追加には、2桁の数十億行(!0+)に達すると時間がかかる場合があります。

開発者は、コアORM機能(より良い列挙型、第2レベルのキャッシュ、一括削除API、より多くのパフォーマンスの挿入と更新など)のようなパフォーマンスなど、簡単に回避できないいくつかの不足している領域に焦点を当てることを望んでいます。実行可能です)。コードファーストは素晴らしいです。コード最初にデータベースを生成して維持することは、非常に単純なシナリオ以外では苦痛です。

1
TomTom

まだこのテーマに興味がある人がいたら、ここに私の解決策を書きます。以下のコードは、add-migrationコマンドの出力を変更します。

public class CustomMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
    protected override void Generate(CreateTableOperation createTableOperation, IndentedTextWriter writer)
    {
        if (createTableOperation.Columns.Any(x => x.Name == "Index") &&
             createTableOperation.Columns.Any(x => x.Name == "Id"))
        {
            if (createTableOperation.PrimaryKey != null)
            {
                createTableOperation.PrimaryKey.IsClustered = false;
            }
        }
        base.Generate(createTableOperation, writer);
    }
}

このジェネレーターを移行構成に登録できます。

internal sealed class Configuration : DbMigrationsConfiguration<Ubrasoft.Freeman.WebApi.Db.MainDb>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
        CodeGenerator = new CustomMigrationCodeGenerator();  
        SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());        
    }

    protected override void Seed(Ubrasoft.Freeman.WebApi.Db.MainDb context)
    {

    }
}

そして、生成された移行コードは次のとおりです。

public override void Up()
    {
        CreateTable(
            "Tenant.Tenant",
            c => new
                {
                    Id = c.Guid(nullable: false),
                    TenantNo = c.Byte(nullable: false),
                    Name = c.String(nullable: false, maxLength: 20),
                    Index = c.Int(nullable: false, identity: true),
                    CreatedDate = c.DateTime(nullable: false, precision: 0, storeType: "datetime2"),
                    UpdatedDate = c.DateTime(nullable: false, precision: 0, storeType: "datetime2"),
                    IsDeleted = c.Boolean(nullable: false),
                })
            .PrimaryKey(t => t.Id, clustered: false)
            .Index(t => t.Index, unique: true, clustered: true);

    } 

ここ はカスタムMigrationCodeGeneratorに関する記事です。

0
Safak Ulusoy