web-dev-qa-db-ja.com

Entity Framework-移行-コードファースト-移行ごとのシード

展開プロセスをクリーンアップするために、移行を検討しています。本番への変更をプッシュする際に必要な手動の介入が少ないほど良いです。

移行システムで3つの大きな障害に遭遇しました。私はそれらの周りのきれいな方法を理解できない場合、それらはショーストッパーです。

1。どうすればSeed移行ごとのデータ:を追加できますか

コマンド「add-migration」を実行します。このコマンドは、UpおよびDown機能を備えた新しい移行ファイルを作成します。次に、UpとDownの両方の変更を使用して、データを自動的に変更します。 SeedデータをConfiguration.Seedメソッドに追加しません。これは、あらゆる種類の複製の問題で終わるすべての移行に対して実行されるためです。

2。上記が不可能な場合、どうすれば重複を避けることができますか?

データベースに値を追加するためにループする列挙型があります。

foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
    context.Access.AddOrUpdate(
        new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
    );
}
context.SaveChanges();

AddOrUpdateを使用していても、データベースに重複があります。上記のコードは、3番目の最終的な問題につながります。

。主キーをシードするにはどうすればよいですか?

上記のコードで列挙可能なものは次のとおりです。

public class Access
{
    public enum Level
    {
        None = 10,
        Read = 20,
        ReadWrite = 30
    }
    public int AccessId { get; set; }
    public string Name { get; set; }
}

プライマリキーとして使用する値を指定していますが、Entity Frameworkはそれを無視しているようです。彼らはまだ1,2,3であることになります。 10,20,30にするにはどうすればよいですか?

現時点ではEFのこれらの制限はありますか、それとも、私が見ていない他の種類の大惨事を防ぐための意図的な制約ですか?

40
Talon
  1. 移行で挿入するデータを修正したら、Sql("Insert ...")の呼び出しを使用して、Up()移行に挿入を直接挿入します。このページの途中の注を参照してください: 固定データを挿入する方法
  2. Seedメソッドでの重複を防ぐには、自然キーを指定する識別子式をとるAddOrUpdateオーバーロードを呼び出します-参照 this answer および このブログエントリ
  3. 整数である主キーは、デフォルトでIDフィールドとして作成されます。別の方法で指定するには、[DatabaseGenerated(DatabaseGeneratedOption.None)]属性を使用します

これは InitializerとSeed methods の良い説明だと思います

AddOrUpdateメソッドの使用方法の例を次に示します。

foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
    context.Access.AddOrUpdate(
        x => x.Name, //the natural key is "Name"
        new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
    );
}
28
Colin

項目1の可能な解決策として、保留中の各移行のSeedメソッドのみを実行するIDatabaseInitializer戦略の実装を作成しました。各自のカスタムIMigrationSeedインターフェイスを実装する必要があります。 DbMigrationクラスの場合、Seedメソッドは、すべての移行クラスのUpおよびDownメソッドの直後に実装されます。

これは、2つの問題を解決するのに役立ちます。

  1. データベースデータの移行(またはシード)によるグループデータベースモデルの移行
  2. Seed移行コードは実際に実行する必要があります。データベース内のデータをチェックするのではなく、作成したデータベースモデルである既知のデータを使用します。

インターフェイスは次のようになります

public interface IMigrationSeed<TContext>
{
    void Seed(TContext context);
}

以下は、このSeedメソッドを呼び出す新しい実装です

public class CheckAndMigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>
    : IDatabaseInitializer<TContext>
    where TContext : DbContext
    where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
{
    public virtual void InitializeDatabase(TContext context)
    {
        var migratorBase = ((MigratorBase)new DbMigrator(Activator.CreateInstance<TMigrationsConfiguration>()));

        var pendingMigrations = migratorBase.GetPendingMigrations().ToArray();
        if (pendingMigrations.Any()) // Is there anything to migrate?
        {
            // Applying all migrations
            migratorBase.Update();
            // Here all migrations are applied

            foreach (var pendingMigration in pendingMigrations)
            {
                var migrationName = pendingMigration.Substring(pendingMigration.IndexOf('_') + 1);
                var t = typeof(TMigrationsConfiguration).Assembly.GetType(
                    typeof(TMigrationsConfiguration).Namespace + "." + migrationName);

                if (t != null 
                   && t.GetInterfaces().Any(x => x.IsGenericType 
                      && x.GetGenericTypeDefinition() == typeof(IMigrationSeed<>)))
                {
                    // Apply migration seed
                    var seedMigration = (IMigrationSeed<TContext>)Activator.CreateInstance(t);
                    seedMigration.Seed(context);
                    context.SaveChanges();
                }
            }
        }
    }
}

ここで良いのは、Seedデータ、標準のEF Seed実装と同じです。ただし、これは奇妙な場合があります。以前の移行でシードされたテーブルを削除することにした場合、既存のSeedコードを適宜リファクタリングする必要があります。

編集:UpとDownの後にシードメソッドを実装する代わりに、同じMigrationクラスの部分クラスを作成できます。これは、再シードしたいときに移行クラスを安全に削除できるので便利です同じ移行。

12

さて、少しのバッシングでEFをサブミッションにバッシングすることができました。ここに私がやったことがあります:

1。特定の移行のデータを確認する方法はありません。すべて共通のConfiguration.Seedメソッドに入力する必要があります。

2。重複を避けるために、2つのことをしなければなりませんでした。列挙型のために、次のシードコードを作成しました。

foreach (var enumValue in Enum.GetValues(typeof(Access.Level)))
{
    var id = (int)enumValue;
    var val = enumValue.ToString();

    if(!context.Access.Any(e => e.AccessId == id))
        context.Access.Add(
            new Access { AccessId = id, Name = val }
        );
}
context.SaveChanges();

基本的に、存在するかどうかを確認し、存在しない場合は追加するだけです

上記が機能するためには、主キー値を挿入できる必要があります。幸いなことに、このテーブルには常に同じ静的データが含まれているため、自動インクリメントを無効にできます。そのためには、コードは次のようになります。

public class Access
{
    public enum Level
    {
        None = 10,
        Read = 20,
        ReadWrite = 30
    }

    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int AccessId { get; set; }
    public string Name { get; set; }
}
3
Talon

こんにちは、私はこのリンクであなたの問題に非常に役立つ情報を見つけました: Safari Books Online

"1。Seed移行ごとのデータ:)を追加するにはどうすればよいですか?"例でわかるように、作成する必要がありますシードのための新しい構成:このシード構成は、移行後に呼び出す必要があります。

_public sealed class Configuration : DbMigrationsConfiguration
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(SafariCodeFirst.SeminarContext context)
    {
        //  This method will be called after migrating to the latest version.

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
        //  to avoid creating duplicate seed data. E.g.
        //
        //    context.People.AddOrUpdate(
        //      p => p.FullName,
        //      new Person { FullName = "Andrew Peters" },
        //      new Person { FullName = "Brice Lambson" },
        //      new Person { FullName = "Rowan Miller" }
        //    );
        //
    }
}
_

"2。上記が不可能な場合、どうすれば重複を避けることができますか?"

AddOrUpdateここでエラーが発生した場合、コールスタックの後に設定エラーが発生する可能性があるため、重複を正確に回避するのに役立ちます。例を参照してください!

"3。主キーをシードするにはどうすればよいですか?"

ここでは、キー定義にも記載されています。キーDatabaseGenerated(DatabaseGeneratedOption.Identity)の場合、提供する必要はありません。他のいくつかのシナリオでは、キータイプに応じて新しいものを作成する必要があります。

「現時点でEFのこれらの制限はあるのか、それとも、私が見ない他の種類の大惨事を防ぐための意図的な制約なのか?」
知らないよ!

3
Bassam Alugili