web-dev-qa-db-ja.com

コードファーストのDbMigratorは、異なるマシンからビルドするときにエラーを引き起こします

SCMの下にプロジェクトがあります。自分のマシンからビルドし、msdeployを介してリモートサーバーに公開すると、すべて正常に機能します。

私の同僚が、リモートサーバーエンティティフレームワーク4.3.1 DbMigratorで、SCMから新たにプルした同じプロジェクトで同じことを試みると、次のようになります。

自動移行は、データが失われる可能性があるため、適用されませんでした。

結局のところ、リモートサーバーに最初に公開したのは「勝者」のようです。データベースをリモートサーバーにドロップすると、同僚が公開でき、ロックアウトされます。私の出版物は上記と同じエラーになります。

DbMigratorの設定は次のようになります。

        var dbMgConfig = new DbMigrationsConfiguration()
        {
            AutomaticMigrationsEnabled = true,
            //***DO NOT REMOVE THIS LINE, 
            //DATA WILL BE LOST ON A BREAKING SCHEMA CHANGE,
            //TALK TO OTHER PARTIES INVOLVED IF THIS LINE IS CAUSING PROBLEMS    
            AutomaticMigrationDataLossAllowed=false,
            //***DO NOT REMOVE THIS LINE,
            ContextType = typeof(TPSContext),
            MigrationsNamespace = "TPS.Migrations",
            MigrationsAssembly = Assembly.GetExecutingAssembly()
        };

これは新しいテーブルと関係があると思います__MigrationHistoryそしてその行に格納された厄介な長い16進文字列。

私は、出版するための全責任を負いたくありません。何に気をつければいいですか?

43
spender

コードを次のように変更しました。

        dbMgConfig.AutomaticMigrationDataLossAllowed = false;
        var mg = new DbMigrator(dbMgConfig);
        mg.Update(null);

        dbMgConfig.AutomaticMigrationDataLossAllowed = true;
        var mg = new DbMigrator(dbMgConfig);
        var scriptor = new MigratorScriptingDecorator(mg);
        string script = scriptor.ScriptUpdate(sourceMigration: null, targetMigration: null);
        throw new Exception(script);

DbMigratorがリモートサーバーでどのような変更を試みているかを観察できるようにします。

この質問の冒頭で概説した場合(つまり、同僚がデータベースを作成するアップロードを作成し、続いて私が別のマシンの同じソースから生成されたアップロードを作成する)、次のSQLステートメントが生成されます。

ALTER TABLE [GalleryImages] DROP CONSTRAINT [FK_GalleryImages_Galleries_Gallery_Id]
ALTER TABLE [GalleryImages] DROP CONSTRAINT [FK_GalleryImages_Images_Image_Id]
ALTER TABLE [UserLightboxes] DROP CONSTRAINT [FK_UserLightboxes_Users_User_Id]
ALTER TABLE [UserLightboxes] DROP CONSTRAINT [FK_UserLightboxes_Lightboxes_Lightbox_Id]
ALTER TABLE [ImageLightboxes] DROP CONSTRAINT [FK_ImageLightboxes_Images_Image_Id]
ALTER TABLE [ImageLightboxes] DROP CONSTRAINT [FK_ImageLightboxes_Lightboxes_Lightbox_Id]
DROP INDEX [IX_Gallery_Id] ON [GalleryImages]
DROP INDEX [IX_Image_Id] ON [GalleryImages]
DROP INDEX [IX_User_Id] ON [UserLightboxes]
DROP INDEX [IX_Lightbox_Id] ON [UserLightboxes]
DROP INDEX [IX_Image_Id] ON [ImageLightboxes]
DROP INDEX [IX_Lightbox_Id] ON [ImageLightboxes]
CREATE TABLE [ImageGalleries] (
   [Image_Id] [int] NOT NULL,
   [Gallery_Id] [int] NOT NULL,
   CONSTRAINT [PK_ImageGalleries] PRIMARY KEY ([Image_Id], [Gallery_Id])
)
CREATE TABLE [LightboxImages] (
   [Lightbox_Id] [int] NOT NULL,
   [Image_Id] [int] NOT NULL,
   CONSTRAINT [PK_LightboxImages] PRIMARY KEY ([Lightbox_Id], [Image_Id])
)
CREATE TABLE [LightboxUsers] (
   [Lightbox_Id] [int] NOT NULL,
   [User_Id] [int] NOT NULL,
   CONSTRAINT [PK_LightboxUsers] PRIMARY KEY ([Lightbox_Id], [User_Id])
)
CREATE INDEX [IX_Image_Id] ON [ImageGalleries]([Image_Id])
CREATE INDEX [IX_Gallery_Id] ON [ImageGalleries]([Gallery_Id])
CREATE INDEX [IX_Lightbox_Id] ON [LightboxImages]([Lightbox_Id])
CREATE INDEX [IX_Image_Id] ON [LightboxImages]([Image_Id])
CREATE INDEX [IX_Lightbox_Id] ON [LightboxUsers]([Lightbox_Id])
CREATE INDEX [IX_User_Id] ON [LightboxUsers]([User_Id])
DROP TABLE [GalleryImages]
DROP TABLE [UserLightboxes]
DROP TABLE [ImageLightboxes]
ALTER TABLE [ImageGalleries] ADD CONSTRAINT [FK_ImageGalleries_Images_Image_Id] FOREIGN KEY ([Image_Id]) REFERENCES [Images] ([Id]) ON DELETE CASCADE
ALTER TABLE [ImageGalleries] ADD CONSTRAINT [FK_ImageGalleries_Galleries_Gallery_Id] FOREIGN KEY ([Gallery_Id]) REFERENCES [Galleries] ([Id]) ON DELETE CASCADE
ALTER TABLE [LightboxImages] ADD CONSTRAINT [FK_LightboxImages_Lightboxes_Lightbox_Id] FOREIGN KEY ([Lightbox_Id]) REFERENCES [Lightboxes] ([Id]) ON DELETE CASCADE
ALTER TABLE [LightboxImages] ADD CONSTRAINT [FK_LightboxImages_Images_Image_Id] FOREIGN KEY ([Image_Id]) REFERENCES [Images] ([Id]) ON DELETE CASCADE
ALTER TABLE [LightboxUsers] ADD CONSTRAINT [FK_LightboxUsers_Lightboxes_Lightbox_Id] FOREIGN KEY ([Lightbox_Id]) REFERENCES [Lightboxes] ([Id]) ON DELETE CASCADE
ALTER TABLE [LightboxUsers] ADD CONSTRAINT [FK_LightboxUsers_Users_User_Id] FOREIGN KEY ([User_Id]) REFERENCES [Users] ([Id]) ON DELETE CASCADE
CREATE TABLE [__MigrationHistory] (
   [MigrationId] [nvarchar](255) NOT NULL,
   [CreatedOn] [datetime] NOT NULL,
   [Model] [varbinary](max) NOT NULL,
   [ProductVersion] [nvarchar](32) NOT NULL,
   CONSTRAINT [PK___MigrationHistory] PRIMARY KEY ([MigrationId])
)
BEGIN TRY
   EXEC sp_MS_marksystemobject '__MigrationHistory'
END TRY
BEGIN CATCH
END CATCH
INSERT INTO [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201203030113082_AutomaticMigration', '2012-03-03T01:13:08.986Z', 0x[removedToShortenPost], '4.3.1')

ご覧のとおり、DbMigratorがスローされる理由は、ブリッジするテーブルの名前を反転することにより、many2many関係の結合に使用される3つのテーブルの名前を変更しようとしているためです(例:GalleryImagesImageGalleriesに、またはUserLightboxesLightboxUsersに)。

回避策

これは、EF 4.3のbugのように見えます。ここでは、「関連付け」テーブルの名前の順序が不確定であるように見えます。これらの種類のテーブルの名前の順序は未定義/不確定であるように思われるため、流暢なAPIを使用して、EFにさまざまなマシンのビルド間で一貫した名前を使用させることで、さまざまな角度からこれに取り組みました。

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder
            .Entity<Gallery>()
            .HasMany(p => p.Images)
            .WithMany(p => p.Galleries)
            .Map(c =>
            {
                c.MapLeftKey("Gallery_Id");
                c.MapRightKey("Image_Id");
                c.ToTable("GalleryImages");
            });
        modelBuilder
            .Entity<User>()
            .HasMany(p => p.Lightboxes)
            .WithMany(p => p.Users)
            .Map(c =>
            {
                c.MapLeftKey("User_Id");
                c.MapRightKey("Lightbox_Id");
                c.ToTable("UserLightboxes");
            });
        modelBuilder
            .Entity<Image>()
            .HasMany(p => p.Lightboxes)
            .WithMany(p => p.Images)
            .Map(c =>
            {
                c.MapLeftKey("Image_Id");
                c.MapRightKey("Lightbox_Id");
                c.ToTable("ImageLightboxes");
            });
    }

これにより、エラーはなくなります。

8
spender
 public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = true;
        } 
2

同じエラーが発生するので、スクリプトを生成してクエリアナライザーで実行しました。それは鍵の長さの問題であることが判明しました:

警告!キーの最大長は900バイトです。インデックス 'PK_dbo .__ MigrationHistory'の最大長は1534バイトです。大きな値の組み合わせによっては、挿入/更新操作が失敗します。

EntityFrameworkチームがそれを認識しているようです。

http://entityframework.codeplex.com/workitem/1216

これがどのような問題を引き起こすのかわからない.....

1
Dave

私もこの問題に遭遇しました。奇妙なことに、問題のテーブルにはデータがまったく含まれていません。つまり、空です。これは、移行を適用した場合にデータ損失が発生することを報告するときに、CodeFirstがチェックしていないようです。

0
John Deighan

Entity Framework6.2.0でこれに似た本当に奇妙なエラーが発生しました。

Configuration.cs:

public class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
        AutomaticMigrationDataLossAllowed = false;
    }
...
}

以下のコードは、あるマシンではStackOverflowExceptionを引き起こしましたが、別のマシンでは正常に機能しました。

var migrator = new DbMigrator(new Configuration());
if (migrator.GetPendingMigrations().Any())
{
    migrator.Update();
}

代わりに次のようにアップグレードして解決しました:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
0
Ogglas