在MVC中使用Entity Framework Code First更新现有数据库

11
在我的MVC应用程序中,我使用了Entity Framework 6,并采用了代码优先方法创建数据库。一段时间后,我通过添加新列和删除一些列来更新了其中一个实体类。为了将这些更改反映到数据库中,我按照以下步骤进行操作:
  1. 删除项目中的迁移文件夹。
  2. 删除数据库中的__MigrationHistory表。
  3. 然后在包管理器控制台中运行以下命令:
    Enable-Migrations -EnableAutomaticMigrations -Force

  4. 在配置文件中添加以下行:
    AutomaticMigrationsEnabled = true;
    AutomaticMigrationDataLossAllowed = true;

  5. 运行:
    Add-Migration Initial

  6. 最后运行:
    Update-Database -Verbose

然而,我遇到了一个错误:“数据库中已经存在一个名为'xxx'的对象。”

为了解决这个问题,我在第5步创建的初始文件的Up方法中注释了代码。这样可以防止错误,但数据库中没有任何更改(更新后的实体表与之前相同)。哪里出错了?感谢您提前的帮助。

这是我在migration.cs文件中注释的Up方法:

    public override void Up()
    {
        CreateTable(
            "dbo.City",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                    RegionID = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.Region", t => t.RegionID)
            .Index(t => t.RegionID);

        CreateTable(
            "dbo.Multiplier",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Status = c.Int(nullable: false),
                    Term = c.Int(nullable: false),
                    CityID = c.Int(nullable: false),
                    WhoIsOnline = c.String(nullable: false),
                    UserId = c.String(nullable: false),
                    InstituteName = c.String(nullable: false),
                    InstituteStatusID = c.Int(nullable: false),
                    InstituteAccreditationDate = c.DateTime(nullable: false),
                    Address = c.String(nullable: false),
                    Phone = c.String(nullable: false),
                    Fax = c.String(),
                    Email = c.String(nullable: false),
                    EurodeskEmail = c.String(nullable: false),
                    WebSite = c.String(),
                    ContactName = c.String(nullable: false),
                    ContactSurname = c.String(nullable: false),
                    ContactJobTitle = c.String(),
                    ContactAssignmentDate = c.DateTime(),
                    ContactWorkingStart = c.String(),
                    ContactWorkingkEnd = c.String(),
                    ContactPhone = c.String(),
                    ContactMobile = c.String(nullable: false),
                    ContactEmail = c.String(nullable: false),
                    ContactCityID = c.Int(nullable: false),
                    LegalRepresentativeName = c.String(nullable: false),
                    LegalRepresentativeSurname = c.String(nullable: false),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.City", t => t.CityID)
            .ForeignKey("dbo.InstituteStatus", t => t.InstituteStatusID)
            .Index(t => t.CityID)
            .Index(t => t.InstituteStatusID);

        CreateTable(
            "dbo.InstituteStatus",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                })
            .PrimaryKey(t => t.ID);

        CreateTable(
            "dbo.TrainingParticipant",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    TrainingID = c.Int(nullable: false),
                    ParticipantID = c.Int(nullable: false),
                    Multiplier_ID = c.Int(),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.Participant", t => t.ParticipantID)
            .ForeignKey("dbo.Training", t => t.TrainingID)
            .ForeignKey("dbo.Multiplier", t => t.Multiplier_ID)
            .Index(t => t.TrainingID)
            .Index(t => t.ParticipantID)
            .Index(t => t.Multiplier_ID);

        CreateTable(
            "dbo.Participant",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                    Surname = c.String(nullable: false),
                    MultiplierID = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.Multiplier", t => t.MultiplierID)
            .Index(t => t.MultiplierID);

        CreateTable(
            "dbo.Training",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                    Date = c.DateTime(nullable: false),
                    CityID = c.Int(nullable: false),
                })
            .PrimaryKey(t => t.ID)
            .ForeignKey("dbo.City", t => t.CityID)
            .Index(t => t.CityID);

        CreateTable(
            "dbo.Region",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(nullable: false),
                })
            .PrimaryKey(t => t.ID);

    }


这是migration.cs文件中的Down方法:

    public override void Down()
    {
        DropForeignKey("dbo.City", "RegionID", "dbo.Region");
        DropForeignKey("dbo.TrainingParticipant", "Multiplier_ID", "dbo.Multiplier");
        DropForeignKey("dbo.TrainingParticipant", "TrainingID", "dbo.Training");
        DropForeignKey("dbo.Training", "CityID", "dbo.City");
        DropForeignKey("dbo.TrainingParticipant", "ParticipantID", "dbo.Participant");
        DropForeignKey("dbo.Participant", "MultiplierID", "dbo.Multiplier");
        DropForeignKey("dbo.Multiplier", "InstituteStatusID", "dbo.InstituteStatus");
        DropForeignKey("dbo.Multiplier", "CityID", "dbo.City");
        DropIndex("dbo.Training", new[] { "CityID" });
        DropIndex("dbo.Participant", new[] { "MultiplierID" });
        DropIndex("dbo.TrainingParticipant", new[] { "Multiplier_ID" });
        DropIndex("dbo.TrainingParticipant", new[] { "ParticipantID" });
        DropIndex("dbo.TrainingParticipant", new[] { "TrainingID" });
        DropIndex("dbo.Multiplier", new[] { "InstituteStatusID" });
        DropIndex("dbo.Multiplier", new[] { "CityID" });
        DropIndex("dbo.City", new[] { "RegionID" });
        DropTable("dbo.Region");
        DropTable("dbo.Training");
        DropTable("dbo.Participant");
        DropTable("dbo.TrainingParticipant");
        DropTable("dbo.InstituteStatus");
        DropTable("dbo.Multiplier");
        DropTable("dbo.City");
    }

这对我很有帮助,因为当更新命令失败时,我的Oracle数据库似乎会删除__MigrationHistory。我会收到一个关于无法处理OracleException的异常,所以我不得不频繁重置。(我仍然不知道为什么Oracle不能处理这些异常) - MrChrister
2个回答

15
为什么你要执行步骤1-4?那就是你犯的错误。如果你已经有了一个先前生成的数据库,只是在更改架构,则只需生成迁移并应用它。通过执行步骤1-4,您实际上正在撤消Entity Framework对此数据库的了解,并最终以现有数据库的代码优先方式结束。此时,您必须手动更改架构或让Entity Framework将其清除并重新开始。
至于回到能够再次应用迁移的状态,您采取了正确的方法,即生成迁移并清空Up方法。但是,您需要针对您应用程序的先前状态执行此操作,即与当前数据库匹配的状态。否则,Entity Framework将生成包含您代码更改的创建表。因此,要按照以下步骤进行:
  1. 将您的代码恢复到修改POCO之前的状态。
  2. 生成迁移。
  3. 删除Up方法中的所有内容。
  4. 使用update-database应用迁移。
  5. 重新应用您对POCO所做的更改。
  6. 生成另一个迁移(此时应该只有添加/更改列语句而不是创建表)
  7. 应用迁移。
之后,您应该可以再次正常工作。然后,下一次您进行代码更改时,只需按照步骤5和6操作即可。

这正是我一直在寻找的 :) 非常感谢您的答案,它将帮助那些真正需要申请以获得EF Code First好处的人。问候... - Jack
谢谢。这个对我在 ASP.NET Core 2 上的开发帮助很大。但我还是有点好奇,我一开始使用了数据库优先(database first)的方法,然后像 Clint Eastwood 那样继续了开发。不知道我应该坚持用“数据库优先”的方式,在数据库中创建一个列,并以某种方式进行脚手架同步(我忘记了具体命令)? - johnny
数据库优先是一种有效的方法,但我认为只有在您有一个DBA来管理它时才适用。除非您是数据库专家,否则最好让EF处理细节,因为它生成的SQL由Microsoft和社区的数据库专业人员进行审核、调整和监控。 - Chris Pratt

6
我曾经遇到过这个问题。值得注意的是,有一些命令可以帮助解决这种情况,即-IgnoreChanges和-Force标志。
我正在从一个多数据库上下文转换到单个数据库上下文的应用程序中。你可以猜到表已经存在,但是新的单个上下文不知道在第二个上下文中维护的表。
实际上,这非常简单(尽管我搜索了2天也没有找到答案,最后我阅读了EF Code First Migrations和包管理器的命令行...)。以下是我的处理方法:
你可以删除迁移文件夹和SQL中的_Migrations表...这将导致你需要使用以下内容: Enable-Migrations -Force 但是你应该能够在不采取激烈措施的情况下继续进行:
  1. 添加迁移 "Reset" -IgnoreChanges -Force(强制忽略您的模型/类中可能存在的更改-适用于使用现有数据库开始)
  2. Update-Database(仅将迁移行写入作为基础)
  3. 添加迁移 "AddMyMigrationsToThisDb" -Force(强制在代码中迭代对象模型以捕获更改)
  4. Update-Database

现在,您应该可以回到仅使用Add-Migration和Update-Database而不需要额外标志的轨道上了。


感谢约翰的回复。 - Jack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接