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

Posted

技术标签:

【中文标题】在 MVC 中使用 Entity Framework Code First 更新现有数据库【英文标题】:Update existing database with Entity Framework Code First in MVC 【发布时间】:2015-02-16 18:25:55 【问题描述】:

在我的 MVC 应用程序中,我使用了 Entity Framework 6 并使用代码优先的方法创建了数据库。一段时间后,我通过添加新列和删除一些列来更新其中一个实体类。为了将这些更改反映到数据库中,我遵循了以下步骤:

    已删除项目中的迁移文件夹。 已删除数据库中的 __MigrationHistory 表。

    然后在包管理器控制台中运行以下命令:Enable-Migrations -EnableAutomaticMigrations -Force

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

    运行:添加迁移初始

    最后,运行: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 不处理异常) 【参考方案1】:

您为什么要执行第 1-4 步?那就是你出错的地方。如果您有一个以前生成的数据库并且您只是对架构进行更改,那么只需生成一个迁移并应用它。通过执行步骤 1-4,您有效地撤销了 Entity Framework 对该数据库的了解,并且基本上以代码优先和现有数据库结束。此时,您要么必须手动更改架构,要么让 Entity Framework 将其淘汰并重新开始。

就回到可以再次应用迁移的状态而言,您在生成迁移并清空 Up 方法方面处于正确的轨道上。但是,您需要针对应用程序的先前状态执行此操作,即与当前数据库匹配的状态。否则,Entity Framework 将生成包含您的代码更改的创建表。所以要遵循的步骤是:

    将代码恢复到开始修改 POCO 之前的状态。 生成迁移。 删除Up 方法中的所有内容 使用update-database 应用迁移 重新应用您对 POCO 所做的更改。 生成另一个迁移(这个迁移现在应该只有添加/更改列语句,而不是创建表) 应用迁移。

在那之后,你应该很高兴再去一次。然后,下次更改代码时,只需按照步骤 5 和 6 进行操作即可。

【讨论】:

这正是我所寻找的正是 :) 非常感谢您的回答,这将帮助那些真正需要申请以首先获得 EF 代码优势的人.问候... 谢谢。这在 ASP.NET Core 2 中帮助了我很多。不过我很好奇。我首先从数据库开始,然后做了克林特伊斯特伍德所做的事情。我是否应该先坚持使用数据库并在数据库中创建一个列并以某种方式与我的脚手架同步(我忘记了命令)? 数据库优先是一种有效的方法,但我只想说,只有当你有一个 DBA 的员工来管理它时。除非您是数据库专家,否则最好让 EF 处理细节,因为它生成的 SQL 由 Microsoft 和社区中的数据库专业人员审查、调整和监控。【参考方案2】:

我遇到了这个确切的问题。似乎值得注意的是,有一些命令可以帮助解决这种情况,即 -IgnoreChanges 和 -Force 标志。

我正在从多 dbContext 修剪到单个 dbContext 应用程序。正如您可以猜到的那样,表已经存在,但是在第二个上下文中维护的表中,单一上下文没有任何新内容。

这实际上很简单(尽管我花了 2 天时间寻找答案但无济于事,让我阅读了 command lines of EF Code First Migrations and the package manager...)这是我的处理方法。

您可以在 SQL 中删除迁移文件夹和 _Migrations 表...这将导致您需要使用以下内容: Enable-Migrations -Force

但你应该可以从这里开始而不采取严厉措施:

    Add-migration “Reset” –IgnoreChanges –Force(强制忽略模型/类中可能存在的更改 – 适合开始使用现有数据库) Update-Database(只写迁移行作为基础) Add-migration “AddMyMigrationsToThisDb” – 强制(强制迭代代码中的对象模型以获取更改) 更新数据库

现在您应该回到正轨,只使用 Add-Migration 和 Update-Database 而没有额外的标志。

【讨论】:

感谢约翰的回复。

以上是关于在 MVC 中使用 Entity Framework Code First 更新现有数据库的主要内容,如果未能解决你的问题,请参考以下文章

MVC5 Entity Framework学习之Entity Framework高级功能

如何将 Entity Framework 4.1 与 MVC 3 登录一起使用

我应该如何在 MVC3 中使用 Code First Entity Framework (4.1) 声明外键关系?

ASP.NET-MVC中Entity和Model之间的关系

如何在 ASP.NET MVC 5、Entity Framework 6 中使用流利的 API 映射表?

使用 Entity Framework 6.1 和 MVC 5 从数据库中使用 Code First 后如何同步模型?