使用EF Core中的迁移来创建和初始化本地数据库

注意:Rider中使用要安装Azure Toolkit for Rider插件。

什么是迁移

模型:Gemini-2.0-Flash-Thinking-Exp-OR,生成时间:2025 03-28 09:43

Entity Framework Core (EF Core) 中的迁移功能是一项 数据库架构管理和版本控制工具。 它允许你:

核心概念: 将你的数据库架构从 C# 代码 (你的实体模型和数据库上下文) 中 演化,并确保数据库与你的模型始终保持同步。

更通俗的解释和类比:

想象一下你的数据库就像一个建筑物的蓝图。 当你修改应用程序的实体类(例如,添加新的属性、更改数据类型、添加新的实体关系),就相当于你想 修改建筑物的蓝图

实体框架迁移就像是一个工具,它帮助你:

  1. 记录蓝图的变更: 每次你修改你的实体模型,迁移功能会记录下这些变化,生成一份详细的 “变更日志” (迁移脚本)。
  2. 应用变更到实际建筑物: 然后,你可以使用这些 “变更日志” (迁移脚本) 来 更新你的实际数据库,使其与最新的蓝图保持一致。
  3. 版本控制你的蓝图: 迁移功能就像数据库的 “版本控制系统”,允许你跟踪所有架构变更的历史记录,并可以回滚到之前的架构版本。

更详细的解释:

1. 它是用来解决什么问题的?

在软件开发过程中,数据库架构通常会随着应用程序逻辑的发展而发生变化。手动管理数据库架构变更(例如,手动编写 SQL 脚本来添加表、列、索引等)非常容易出错且耗时,尤其是在团队协作开发中。

EF Core 迁移功能解决了这些问题,它提供了一种 代码优先 (Code-First) 的方式来管理数据库架构:

  • 自动化数据库更改: 你只需要修改你的 C# 实体模型,迁移功能会自动生成相应的 SQL 脚本来更新数据库。
  • 版本控制数据库架构: 每个迁移都代表数据库架构的一个 “版本”。 你可以轻松地查看、应用和回滚迁移,就像代码版本控制一样。
  • 支持团队协作: 每个人都可以同步最新的数据库架构,避免因数据库架构不一致而导致的问题。
  • 简化部署和环境同步: 迁移脚本可以用于轻松地将数据库架构部署到不同的环境 (开发、测试、生产等) 并保持一致性。

2. 迁移的工作流程:

典型的 EF Core 迁移工作流程如下:

  • 修改你的实体模型: 你在你的 C# 代码中更改你的实体类、DbContext 类,以反映你需要的数据库架构变更。
  • 添加迁移 (Add-Migration): 运行 Add-Migration <迁移名称> 命令(例如:Add-Migration AddBlogUrl)。 EF Core 会比较当前的实体模型与上次迁移应用后的模型,然后生成一个新的 迁移文件集,通常包含:
    • <迁移名称>.cs (C# 代码): 包含 C# 代码来描述数据库变更,主要包括 Up()Down() 方法。
    • Up() 方法: 定义 应用 迁移时执行的操作 (例如,创建表、添加列)。
    • Down() 方法: 定义 回滚 迁移时执行的操作 (例如,删除表、删除列)。 Down() 方法通常是 Up() 方法的反向操作。
    • <迁移名称>.Designer.cs (C# 代码): 包含迁移的元数据,通常不需要手动修改。
  • 检查和自定义迁移脚本: 你可以检查生成的 Up()Down() 方法中的 C# 代码,并根据需要进行自定义(例如,添加数据迁移、微调 SQL 操作)。 EF Core 最终会根据这些 C# 代码生成数据库特定的 SQL 脚本。
  • 更新数据库 (Update-Database): 运行 Update-Database 命令。 EF Core 会:
    • 检查已经应用的迁移: EF Core 会在数据库中创建一个名为 __EFMigrationsHistory 的表来跟踪已应用的迁移。
    • 应用新的、尚未应用的迁移: 对于每个新的迁移,EF Core 会执行 Up() 方法中定义的数据库操作,并将该迁移记录到 __EFMigrationsHistory 表中。
  • 生成 SQL 脚本 (Script-Migration): 运行 Script-Migration 命令来生成 SQL 脚本,而不是直接应用到数据库。 这对于在生产环境或需要手动审核 SQL 的情况下非常有用。 你可以指定要生成脚本的迁移范围 (例如,从某个迁移到最新的迁移,或者生成所有迁移的脚本)。

3. 迁移命令 (常用命令):

  • Add-Migration <迁移名称>: 创建新的迁移。
  • Remove-Migration: 移除最后一次添加的迁移(如果尚未应用到数据库)。
  • Update-Database: 将新的迁移应用到数据库。
  • Script-Migration: 生成 SQL 脚本。
  • Script-DbContext: 生成创建完整数据库架构的 SQL 脚本(用于初始数据库创建)。
  • Get-Migration: 列出可用的迁移。
  • Get-DbContext: 列出可用的 DbContext 类。

4. 示例:

假设你有一个 Blog 实体,开始时只有一个 Title 属性:

public class Blog
{
    public int BlogId { get; set; }
    public string Title { get; set; }
}

后来你决定添加一个 Url 属性:

public class Blog
{
    public int BlogId { get; set; }
    public string Title { get; set; }
    public string Url { get; set; } // 新增 Url 属性
}

你需要执行以下步骤来将这个修改同步到数据库:

  1. 打开 Package Manager Console (或终端)。
  2. 导航到你的项目目录。
  3. 运行命令: Add-Migration AddBlogUrl (迁移名称可以自定义,例如 “AddBlogUrlProperty”)
  4. 检查生成的迁移文件 (例如 AddBlogUrl.cs )。 你会看到 Up() 方法中包含了添加 Url 列的代码,Down() 方法中包含了删除 Url 列的代码。
  5. 运行命令: Update-Database

现在你的数据库中的 Blogs 表就会多出一个 Url 列。

5. 总结迁移的优势:

  • 数据库版本控制: 像代码一样管理数据库架构,方便历史追溯和版本回滚。
  • 代码优先开发: 通过修改 C# 代码驱动数据库架构变更,更符合现代开发流程。
  • 自动化和减少错误: 自动生成 SQL 脚本,减少手动编写 SQL 的错误和复杂性。
  • 环境一致性: 方便地将相同的数据库架构部署到不同的环境中。
  • 团队协作友好: 易于团队成员同步和共享数据库架构变更。
  • 平滑数据库演进: 随着应用程序的发展,可以逐步演化数据库架构,而无需手动管理复杂的 SQL 脚本。

总而言之,EF Core 迁移是现代 .NET 开发中管理数据库架构的强大且必不可少的工具,它极大地提高了开发效率和数据库管理的可靠性。

操作示例

示例来自: 教程:使用 ASP.NET Core 创建 Razor Pages Web 应用 | Microsoft Learn

添加数据模型

右键单击RazorPagesMovie项目>“添加”>“新建文件夹”, 将该文件夹命名为 Models ,然后右键单击 Models 文件夹,选择“添加”>“类” 。,将类命名为 Movie

Movie 类添加以下属性(Property):

什么是属性,参考:C#中的Property属性

using System.ComponentModel.DataAnnotations;

namespace RazorPagesTestMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Movie 类包含:

  • 数据库需要 ID 字段以获取主键。
  • [DataType] 属性,用于指定 ReleaseDate 属性中的数据类型。 通过此特性:
    • 用户无需在日期字段中输入时间信息。
    • 仅显示日期,而非时间信息。
  • string 后的问号表示属性可为空。

搭建模型框架

在此部分,将搭建“电影”模型的基架。 确切地说,基架工具将生成页面,用于对“电影”模型执行创建、读取、更新和删除 (CRUD) 操作。

右键单击 Pages 文件夹 “添加”>“新建文件夹”,将文件夹命名为 Movies

右键单击 Pages / Movies 文件夹,选择“添加”>“已搭建基架的新项”:

rider中叫基架条目。

在“添加新基架”对话框中,依次选择“RazorPages”>“使用实体框架的 Pages (CRUD)”,点击添加:

英文全称是:Razor Pages using Entity Framework (CRUD)。

然后选择之前创建好的类 Movie (RazorPagesTestMovie.Models) ,然后点击数据上下文类右侧的添加按钮,选择自动生成的类名,勾选“引用脚本库”和“使用布局页面”:

同时,appsettings.json 文件中通过用于连接到本地数据的连接字符串也会 自动进行更新

本文使用不需要对用户进行身份验证的本地数据库。 生产应用应使用可用的最安全的身份验证流。 有关已部署测试和生产应用的身份验证的详细信息,请参阅 安全身份验证流

添加之后,新增了如下几个文件:

  • Pages / Movies:“创建”、“删除”、“详细信息”、“编辑”和“索引”。
  • Data / RazorPagesMovieContext.cs

修改 Program.cs 文件:

using Microsoft.EntityFrameworkCore;
// using Microsoft.Extensions.DependencyInjection;
using RazorPagesTestMovie.Data;
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddDbContext<RazorPagesTestMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("RazorPagesTestMovieContext") ?? throw new InvalidOperationException("Connection string 'RazorPagesTestMovieContext' not found.")));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.MapStaticAssets();
app.MapRazorPages()
    .WithStaticAssets();

app.Run();

初始化数据库

Entity Framework Core 中的迁移功能提供了一种方法来执行以下操作:

  • 创建初始数据库架构。
  • 以增量的方式更新数据库架构,使其与应用的数据模型保持同步。 保存数据库中的现有数据。

visual studio中的操作如下:

在此部分中,程序包管理器控制台 (PMC) 窗口用于:

  • 添加初始迁移。
  • 使用初始迁移来更新数据库。
  • 在“工具”菜单中,选择“NuGet 包管理器”“包管理器控制台”

输入命令:

Add-Migration InitialCreate
  • Add-Migration 命令生成用于创建初始数据库架构的代码。 该架构基于在 DbContext 中指定的模型。 InitialCreate 参数用于为迁移命名。 可以使用任何名称,但是按照惯例,会选择可说明迁移的名称。

可能会显示警告:

No type was specified for the decimal column 'Price' on entity type 'Movie'. This will cause values to be silently truncated if they do not fit in the default precision and scale. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'.

不重要,下文再说。

继续在 PMC 中,输入以下命令:

Update-Database

Update-Database 命令在尚未应用的迁移中运行 Up 方法。 在这种情况下,命令在用于创建数据库的 Up 文件中运行 Migrations/<time-stamp>_InitialCreate.cs 方法。

rider中的操作不太一样:

点击“工具”栏,“Entity Framework Core”,点击“添加迁移”:

输入迁移名称 InitialCreate

同样会有警告:

但是会显示 succeeded 成功,然后点击更新数据库:

选项默认即可:

数据上下文 RazorPagesMovieContext

  • 派生自 Microsoft.EntityFrameworkCore.DbContext
  • 指定数据模型中包含哪些实体。
  • 为 EF Core 模型协调 Movie 功能,例如“创建”、“读取”、“更新”和“删除”。

生成的文件 RazorPagesMovieContext 中的类 Data / RazorPagesMovieContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using RazorPagesMovie.Models;

namespace RazorPagesMovie.Data
{
    public class RazorPagesMovieContext : DbContext
    {
        public RazorPagesMovieContext (DbContextOptions<RazorPagesMovieContext> options)
            : base(options)
        {
        }

        public DbSet<RazorPagesMovie.Models.Movie> Movie { get; set; } = default!;
    }
}

前面的代码为实体集创建 DbSet<Movie> 属性。 在实体框架术语中,实体集通常与数据表相对应。 实体对应表中的行。

通过调用 DbContextOptions 对象中的一个方法将连接字符串名称传递到上下文。 进行本地开发时,配置系统appsettings.json 文件中读取连接字符串。