ASP.NET Core Razor Pages 教程四 创建迁移

创建迁移

开发应用程序时,随着新要求的出现模型可能会经常发生变化。数据库则需要与模型保持同步。通过 Entity Framework Core 迁移功能,你可以对模型进行更改,然后把这些更改传送到数据库架构。如果数据库不存在,迁移功能也可以用于数据库创建。

配置模型

EF Core 迁移基于一组约定。这些约定控制着 DbSet 对象映射到数据库中的表的方式,属性映射到列的方式,.NET 数据类型如何映射到数据库提供程序公开的适当类型以及如何创建和管理键和索引。有些时候这些约定无法满足你的需求,或者 EF Core 不能确定你的意图。在这种情况下,你可以使用配置来告诉 EF Core 你想要什么。

配置可以通过两种方式应用:通过属性(attributes)来装饰类及其属性(properties), 或都使用 Fluent API。 属性(attributes)仅提供配置选项的子集。 所以对于任何相当复杂的模型,你可能需要依赖 Fluent API 进行配置。 因此,对所有配置使用 Fluent API 是有意义的,从而保持配置代码的一致性,从而更容易在一个地方进行推理。

那么你应该将 Fluent API 配置代码放在哪里呢?

你有两个选项:可以将其直接放在 DbContext 类的 OnModelCreating 方法中; 或者可以将配置代码放在每个实体的不同类上。 本示例将演示后一种方法, 因为这是推荐的管理方法。

Data 文件夹中添加一个名为 Configurations 的新文件夹。 然后在 Configurations 文件夹中添加一个名为 ProductConfiguration.cs 新的 C# 类文件。文件内容替换为以下所示代码:

using Bakery.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace Bakery.Data.Configurations
{
    public class ProductConfiguration : IEntityTypeConfiguration<Product>
    {
        public void Configure(EntityTypeBuilder<Product> builder)
        {
            builder.Property(p => p.ImageName).HasColumnName("ImageFileName");
        }
    }
}

该类实现了 IEntityTypeConfiguration<TEntity> 接口,该接口有一个方法:Configure。 配置是在这个方法中定义的。 在这个实例中, ImageName 属性被映射到名为 "ImageFileName" 的列。 默认行为是映射到与属性名称相同的列。

配置类注册在 BakeryContextOnModelCreating 方法中, 如以下突出显示的部分所示:

public class BakeryContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite(@"Data source=Bakery.db");
    }
     
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new ProductConfiguration());
    }
}

创建种子数据

原始的 Bakery 模板包含一个预先填充产品数据的数据库。当你第一次创建数据库的时候,它是空的 - 除非你在迁移执行时用数据对它进行“播种”。在本节中,您将使用 EF Core(从2.1版本开始)提供的 API 来实现这一点。

在 Data 文件夹中创建一个名为 ModelBuilderExtensions.cs 的 C# 类文件。 将其内容替换为以下所示代码:

using Bakery.Models;
using Microsoft.EntityFrameworkCore;

namespace Bakery.Data
{
    public static class ModelBuilderExtensions
    {
        public static ModelBuilder Seed(this ModelBuilder modelBuilder){

            modelBuilder.Entity<Product>().HasData(
                new Product
                {
                    Id = 1,
                    Name = "Carrot Cake",
                    Description = "A scrumptious mini-carrot cake encrusted with sliced almonds",
                    Price = 8.99m,
                    ImageName = "carrot_cake.jpg"
                },
                new Product
                {
                    Id = 2,
                    Name = "Lemon Tart",
                    Description = "A delicious lemon tart with fresh meringue cooked to perfection",
                    Price = 9.99m,
                    ImageName = "lemon_tart.jpg"
                },
                new Product
                {
                    Id = 3,
                    Name = "Cupcakes",
                    Description = "Delectable vanilla and chocolate cupcakes",
                    Price = 5.99m,
                    ImageName = "cupcakes.jpg"
                },
                new Product
                {
                    Id = 4,
                    Name = "Bread",
                    Description = "Fresh baked French-style bread",
                    Price = 1.49m,
                    ImageName = "bread.jpg"
                },
                new Product
                {
                    Id = 5,
                    Name = "Pear Tart",
                    Description = "A glazed pear tart topped with sliced almonds and a dash of cinnamon",
                    Price = 5.99m,
                    ImageName = "pear_tart.jpg"
                },
                new Product
                {
                    Id = 6,
                    Name = "Chocolate Cake",
                    Description = "Rich chocolate frosting cover this chocolate lover's dream",
                    Price = 8.99m,
                    ImageName = "chocolate_cake.jpg"
                }
            );
            return modelBuilder;
        }
    }
}

Seed 方法是 ModelBuilder 类型的扩展方法,它被传递给前面使用过的 OnModelCreating 方法。 该方法的主体使用 EF Core 2.1 引入的 HasData 方法将给定的实体配置为具有种子数据。 为每个实体提供的值与原始模板中的值相对应并包含键值。 如果将值作为INSERT语句的一部分提供, SQLite将愉快地接受自动递增列中的值。另一方面,SQL Server 将在目标表上启用 IDENTITY_INSERT, 然后在添加种子数据之后再关闭它。

OnModelCreating 方法中调用种子方法本身。 它返回 ModelBuilderType 的一个实例, 因此它可以链接到其他也返回 ModelBuilder 类型的调用。 ApplyConfiguration 符合这个要求,因此您可以将 Seed 方法链接到它:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyConfiguration(new ProductConfiguration()).Seed();
}

创建迁移

现在模型配置已经完成并准备好了种子数据, 你可以创建实际的迁移了。 从终端执行以下的命令:

dotnet ef migrations add CreateDatabase --output-dir Data/Migrations

这将创建一个名为 CreateDatabase 的迁移。 迁移的文件生成在 Data 文件夹中新创建的名为 Migrations 的文件夹中(由传递给 output-dir 开关的值指定):
migrations

第一个文件包含要转换为 SQL 语句的 C# 代码, 而快照文件包含当前模型的 C# 表示。 后续迁移使用此选项来计算更新数据库架构所需的更改。

执行迁移

在配置和创建了迁移之后,现在是执行迁移的时候了。在终端输入以下命令,按回车键执行命令:

dotnet ef database update

一旦您确认迁移已经被应用,你应该会看到在站点根目录中创建的 Baker.db 文件:
db

你可以使用适当的实用程序打开它。 我使用 DB Browser For SQLite 。 你可以浏览生成的表。__EFMigrationsHistory 表包含每次迁移的详细信息。 Products 表包含了你播种到其中的数据:
Database

摘要

您已经学习了如何在迁移之前配置模型、生成种子数据,然后创建迁移并执行它。现在您已经有了一个包含数据的数据库,可以开始使用它了。

posted on 2019-02-23 13:54  路盟  阅读(416)  评论(0编辑  收藏  举报

导航