EF 4.3 Code-Based Migrations Walkthrough

本文介绍了如何使用 Entity Framework 的 CodeFirst 迁移来更新数据库模式,并通过实例演示了迁移的创建、定制及数据迁移的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Building an Initial Model & Database

Before we start using migrations we need a project and a Code First model to work with. For this walkthrough we are going to use the canonical Blog and Post model.

  1. Create a new MigrationsCodeDemo Console application.      
    .
  2. Add the latest version of the EntityFramework NuGet package to the project.      
    • Tools –> Library Package Manager –> Package Manager Console.
    • Run the ‘Install-Package EntityFramework’ command.          
      .
  3. Add a Model.cs file with the code shown below. This code defines a single Blog class that makes up our domain model and a BlogContext class that is our EF Code First context.      
    using System.Data.Entity;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.Data.Entity.Infrastructure;
    
    namespace MigrationsCodeDemo
    {
        public class BlogContext : DbContext
        {
            public DbSet<Blog> Blogs { get; set; }
        }
    
        public class Blog
        {
            public int BlogId { get; set; }
            public string Name { get; set; }
        }
    }
  4. Now that we have a model it’s time to use it to perform data access. Update the Program.cs file with the code shown below.      

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace MigrationsCodeDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (var db = new BlogContext())
                {
                    db.Blogs.Add(new Blog { Name = "Another Blog " });
                    db.SaveChanges();
    
                    foreach (var blog in db.Blogs)
                    {
                        Console.WriteLine(blog.Name);
                    }
                }
            }
        }
    }
  5. Run your application and you will see that a MigrationsCodeDemo.BlogContext database is created on your local SQLEXPRESS instance.      

    MigrationsCodeDemoDatabase

 

Enabling Migrations

It’s time to make some more changes to our model.

  1. Let’s introduce a Url property to the Blog class.    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
    }
    
  2. If you were to run the application again you would get an InvalidOperationException because the database no longer matches your model.    

    ”The model backing the 'BlogContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).”      
    .
  3. As the exception suggests, it’s time to start using Code First Migrations. The first step is to enable migrations for our context.    
    • Run the ‘Enable-Migrations’ command in Package Manager Console.        
      . .
  4. This command has added a Migrations folder to our project. This new folder contains two files:    
    • The Configuration class. This class allows you to configure how Migrations behaves for your context. For this walkthrough we will just use the default configuration.        
      Because there is just a single Code First context in your project, Enable-Migrations has automatically filled in the context type that this configuration applies to.
    • An InitialCreate migration. This migration was generated because we already had Code First create a database for us, before we enabled migrations. The code in this scaffolded migration represents the objects that have already been created in the database. In our case that is the Blog table with a BlogId and Name columns.

Your First Migration

Code First Migrations has two commands that you are going to become familiar with.

  • Add-Migration will scaffold the next migration based on changes you have made to your model.
  • Update-Database will apply any pending changes to the database.
  1. We need to scaffold a migration to take care of the new Url property we have added. The Add-Migration command allows us to give these migrations a name, let’s just call ours AddBlogUrl.    
    • Run the ‘Add-Migration AddBlogUrl’ command in Package Manager Console.        
      .
  2. In the Migrations folder we now have a new AddBlogUrl migration. The migration filename is pre-fixed with a timestamp to help with ordering.    
    namespace MigrationsCodeDemo.Migrations
    {
        using System.Data.Entity.Migrations;
        
        public partial class AddBlogUrl : DbMigration
        {
            public override void Up()
            {
                AddColumn("Blogs", "Url", c => c.String());
            }
            
            public override void Down()
            {
                DropColumn("Blogs", "Url");
            }
        }
    }
  3. We could now edit or add to this migration but everything looks pretty good. Let’s use Update-Database to apply this migration to the database.    
    • Run the ‘Update-Database’ command in Package Manager Console.        
      .
  4. Code First Migrations has now updated the MigrationsCodeDemo.BlogContext database to include the Url column in the Blogs table. 

    MigrationsCodeDemoDatabaseUpdated

Customizing Migrations

So far we’ve generated and run a migration without making any changes. Now let’s look at editing the code that gets generated by default.

  1. It’s time to make some more changes to our model, let’s introduce a Blog.Rating property and a new Post class.    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }     
        public string Url { get; set; } 
        public int Rating { get; set; }
    
        public List<Post> Posts { get; set; }
    }
    
    public class Post
    {
        public int PostId { get; set; }
        [MaxLength(200)]
        public string Title { get; set; }
        public string Content { get; set; }
    
        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }  
    
  2. Let’s use the Add-Migration command to let Code First Migrations scaffold its best guess at the migration for us. We’re going to call this migration AddPostClass.    
    • Run the ‘Add-Migration AddPostClass’ command in Package Manager Console.        
      .
  3. Code First Migrations did a pretty good job of scaffolding these changes, but there are some things we might want to change:    
    • First up, let’s add a unique index to Posts.Title column.
    • We’re also adding a non-nullable Blogs.Rating column, if there is any existing data in the table it will get assigned the CLR default of the data type for new column (Rating is integer, so that would be 0). But we want to specify a default value of 3 so that existing rows in the Blogs table will start with a decent rating.        

      (These changes to the scaffolded migration are highlighted)        

      namespace MigrationsCodeDemo.Migrations
      
      {
          using System.Data.Entity.Migrations;
      
          public partial class AddPostClass : DbMigration
          {
              public override void Up()
              {
                  CreateTable(
                      "Posts",
                      c => new
                          {
                              PostId = c.Int(nullable: false, identity: true),
                              Title = c.String(maxLength: 200),
                              Content = c.String(),
                              BlogId = c.Int(nullable: false),
                          })
                      .PrimaryKey(t => t.PostId)
                      .ForeignKey("Blogs", t => t.BlogId, cascadeDelete: true)
                      .Index(t => t.BlogId)
                      .Index(p => p.Title, unique: true);
      
                  AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
              }
      
              public override void Down()
              {
                  DropIndex("Posts", new[] { "BlogId" });
                  DropForeignKey("Posts", "BlogId", "Blogs");
                  DropColumn("Blogs", "Rating");
                  DropTable("Posts");
              }
          }
      }
  4. Our edited migration is looking pretty good, so let’s use Update-Database to bring the database up-to-date. This time let’s specify the –Verbose flag so that you can see the SQL that Code First Migrations is running.    
    • Run the ‘Update-Database –Verbose’ command in Package Manager Console.

    Data Motion / Custom SQL

    So far we have looked at migration operations that don’t change or move any data, now let’s look at something that needs to move some data around. There is no native support for data motion yet, but we can run some arbitrary SQL commands at any point in our script.

    1. Let’s add a Post.Abstract property to our model. Later, we’re going to pre-populate the Abstract for existing posts using some text from the start of the Content column.    
      public class Post
      {
          public int PostId { get; set; }
          [MaxLength(200)]
          public string Title { get; set; }
          public string Content { get; set; }
          public string Abstract { get; set; }     
      
          public int BlogId { get; set; }
          public Blog Blog { get; set; }
      }
    2. Let’s use the Add-Migration command to let Code First Migrations scaffold its best guess at the migration for us. We’re going to call this migration AddPostAbstract.    
      • Run the ‘Add-Migration AddPostAbstract’ command in Package Manager Console.        
        .
    3. The generated migration takes care of the schema changes but we also want to pre-populate the Abstract column using the first 100 characters of content for each post. We can do this by dropping down to SQL and running an UPDATE statement after the column is added.    
      namespace MigrationsCodeDemo.Migrations
      {
          using System.Data.Entity.Migrations;
          
          public partial class AddPostAbstract : DbMigration
          {
              public override void Up()
              {
                  AddColumn("Posts", "Abstract", c => c.String());
      
                  Sql("UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
              }
              
              public override void Down()
              {
                  DropColumn("Posts", "Abstract");
              }
          }
      }
    4. Our edited migration looks good, so let’s use Update-Database to bring the database up-to-date. We’ll specify the –Verbose flag so that we can see the SQL being run against the database.

      • Run the ‘Update-Database –Verbose’ command in Package Manager Console.

    Migrate to a Specific Version (Including Downgrade)

    So far we have always upgraded to the latest migration, but there may be times when you want upgrade/downgrade to a specific migration.

    1. Let’s say we want to migrate our database to the state it was in after running our AddBlogUrl migration. We can use the –TargetMigration switch to downgrade to this migration.    
      • Run the ‘Update-Database –TargetMigration:"AddBlogUrl"’ command in Package Manager Console.

    This command will run the Down script for our AddBlogAbstract and AddPostClass migrations.

    If you want to roll all the way back to an empty database then you can use the Update-Database –TargetMigration:$InitialDatabase command.

    Getting a SQL Script

    If another developer wants these changes on their machine they can just sync once we check our changes into source control. Once they have our new migrations they can just run the Update-Database command to have the changes applied locally. However if we want to push these changes out to a test server, and eventually production, we probably want a SQL script we can hand off to our DBA.

    1. Let’s run the Update-Database command but this time we’ll specify the –Script flag so that changes are written to a script rather than applied. We’ll also specify a source and target migration to generate the script for. We want a script to go from an empty database ($InitialDatabase) to the latest version (migration “AddPostAbstract”).    

      Note: If you don’t specify a target migration, Migrations will use the latest migration as the target.

      • Run the ‘Update-Database -Script -SourceMigration:$InitialDatabase -TargetMigration:"AddPostAbstract"’ command in Package Manager Console.        
        .
    2. Code First Migrations will run the migration pipeline but instead of actually applying the changes it will write them out to a .sql file for you. Once the script is generated, it is opened for you in Visual Studio, ready for you to view or save.

    Summary

    In this walkthrough you saw how to scaffold, edit and run code-based migrations to upgrade and downgrade your database. You also saw how to get a SQL script to apply migrations to a database.

    <think>我们正在处理EntityFramework中的异常:System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException。这个异常通常发生在尝试使用自动迁移,但自动迁移被禁用的情况下。根据引用[5]:在Configuration.cs类中,我们可以设置自动迁移相关的属性:AutomaticMigrationsEnabled:设置为true以启用自动迁移。AutomaticMigrationDataLossAllowed:设置为true以允许在自动迁移过程中可能发生的数据丢失(否则,如果可能发生数据丢失,会抛出异常)。因此,解决这个异常的方法是在迁移配置中启用自动迁移。步骤:1.打开项目中的迁移配置类(通常位于Migrations文件夹下,名为Configuration.cs)。2.在配置类的构造函数中,设置AutomaticMigrationsEnabled为true。3.如果需要允许自动迁移期间可能发生的数据丢失,则设置AutomaticMigrationDataLossAllowed为true(注意:这可能会导致数据丢失,请谨慎使用)。示例代码:```csharpinternalsealedclassConfiguration:DbMigrationsConfiguration<YourDbContext>{publicConfiguration(){AutomaticMigrationsEnabled=true;//启用自动迁移AutomaticMigrationDataLossAllowed=true;//如果允许数据丢失,则设置为true}//可以有Seed方法等}```但是,请注意:-自动迁移可能会在某些情况下导致数据丢失(例如删除列或表)。如果AutomaticMigrationDataLossAllowed设置为false(默认),那么当检测到可能的数据丢失时,就会抛出异常。-推荐的做法是使用代码迁移(即生成迁移脚本并检查)而不是自动迁移,特别是在生产环境中。如果不想使用自动迁移,那么应该通过生成显式迁移并更新数据库的方式来进行:1.在程序包管理器控制台中运行命令:`Enable-Migrations`(如果尚未启用迁移)。2.然后使用`Add-Migration[迁移名称]`来生成迁移代码。3.检查生成的迁移代码,确认无误后使用`Update-Database`来应用迁移。因此,如果遇到此异常,有两个解决方案:方案一:启用自动迁移(如上所述)。方案二:不使用自动迁移,而是使用显式迁移命令(Add-Migration和Update-Database)。根据引用[4]中提到的自定义迁移生成器,我们还可以扩展迁移操作,但这通常不是解决此异常所必需的。另外,引用[2]和引用[3]提到的错误可能与程序集版本或实现有关,但当前问题主要是由于自动迁移被禁用导致的,因此先解决自动迁移的设置问题。如果问题仍然存在,请检查:-确保配置类被正确设置(例如,在应用程序启动时设置数据库迁移初始化策略)。-在DbContext的构造函数中,可以设置初始化策略,例如:Database.SetInitializer(newMigrateDatabaseToLatestVersion<YourDbContext,Configuration>());这样,应用程序在启动时会自动将数据库迁移到最新版本。总结:解决AutomaticMigrationsDisabledException异常的方法是启用自动迁移,或者改用显式迁移。生成相关问题:1.如何启用EntityFramework的自动迁移?2.使用自动迁移时如何避免数据丢失?3.如何为EntityFramework配置数据库初始化策略?4.如何生成和执行显式迁移?5.在EntityFramework中,自动迁移和显式迁移有什么区别?</think>### 解决 `AutomaticMigrationsDisabledException` 异常的方法 该异常表示 Entity Framework 检测到数据库模型变化,但**自动迁移功能被禁用**。以下是完整解决方案: #### 1. **启用自动迁移** 在迁移配置文件 `Configuration.cs` 中设置关键属性: ```csharp internal sealed class Configuration : DbMigrationsConfiguration<YourDbContext> { public Configuration() { // 核心修复:启用自动迁移 AutomaticMigrationsEnabled = true; // 可选:允许自动迁移期间数据丢失 AutomaticMigrationDataLossAllowed = true; } } ``` > **注意**:`AutomaticMigrationDataLossAllowed = true` 仅在确认可接受数据丢失时使用(如开发环境)[^5]。 #### 2. **显式迁移替代方案(推荐生产环境)** 若需严格避免数据丢失: ```bash # PowerShell 命令(程序包管理器控制台) Add-Migration YourMigrationName # 生成迁移脚本 Update-Database # 应用迁移 ``` 此方法允许手动审查 SQL 变更脚本后再执行[^5]。 #### 3. **初始化策略配置** 在应用启动时(如 `Global.asax` 或 `Program.cs`)添加: ```csharp Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourDbContext, Configuration>()); ``` 此代码强制 EF 在启动时检查迁移状态[^5]。 #### 4. **程序集冲突处理** 若引用中出现 `EntityContainerMapping` 类型加载错误[^2]: - 检查所有项目使用**相同 EF 版本** - 清理解决方案并重建 - 删除 `bin`/`obj` 文件夹后重新编译 #### 5. **自定义迁移生成器(高级)** 如需深度控制迁移逻辑(如引用[4]示例),继承 `CSharpMigrationOperationGenerator` 并覆写方法: ```csharp public class CustomMigrationGenerator : CSharpMigrationOperationGenerator { protected override void Generate(AlterColumnOperation op, IndentedTextWriter writer) { // 自定义列变更处理逻辑 } } ``` --- ### 关键原因分析 | 异常原因 | 解决方案 | 适用场景 | |----------|----------|----------| | `AutomaticMigrationsEnabled=false` | 设为 `true` | 快速开发迭代 | | 模型变更未生成迁移脚本 | 执行 `Add-Migration` | 生产环境 | | 未配置数据库初始化策略 | `SetInitializer` | 所有环境 | | EF程序集版本冲突 | 统一NuGet包版本 | 多项目解决方案 | > **最佳实践**:生产环境始终使用显式迁移(`Add-Migration`),开发环境可启用自动迁移[^5]。 ---
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值