C# Entity Framework Core Migrations 最佳实践

Posted

技术标签:

【中文标题】C# Entity Framework Core Migrations 最佳实践【英文标题】:C# Entity Framework Core Migrations best praxis 【发布时间】:2022-01-11 11:21:57 【问题描述】:

我有一个小程序,它使用EFCore 5.0.12,我还在程序中添加了一个公司和个人模型,它们是我的数据库模型。然后我添加了一个迁移来初始创建表。

然后我为 Person 模型添加了一个 Address 模型,它也应该是一个 DB-Model。进行了第二次迁移,您可以在下面看到。但是,如果我再次运行我的代码,它似乎想要迁移两个迁移。

如何从特定迁移中迁移数据库。

这是我的代码:

这是我的 TestDbContext,它应该完成所有 Db 工作。

public class TestDbContext : DbContext

    public DbSet<Company> Companies  get; set; 

    private string _dbPath = Path.Combine(".", "TestDb.sqlite");

    public TestDbContext() 
    
        if (File.Exists(_dbPath))
            Database.Migrate();
        else Database.EnsureCreated();
    

    protected override void OnConfiguring(Microsoft.EntityFrameworkCore.DbContextOptionsBuilder optionsBuilder)
    
        if (!Directory.Exists(Path.GetDirectoryName(_dbPath)))
            Directory.CreateDirectory(Path.GetDirectoryName(_dbPath));

        try
        
            optionsBuilder.UseSqlite($"Data Source=Path.GetFullPath(_dbPath)");
        
        catch (Exception ex)
        
            //Log.Error("Could not create DB");
            //Log.Error(ex);

            return;
        
    

我还得到了这个继承自 BaseEntry 的 Company-Model,它拥有一个 ID 字段

public class Company : BaseEntry

    public List<Person> Employees  get; set;  = new List<Person>();

Person-Model 也继承自 BaseEntry 以获取 PrimaryKey。

public class Person : BaseEntry

    public string Name  get; set; 
    public string Firstname  get; set; 
    public Address Address  get; set; 

迁移时应添加的地址。

public class Address : BaseEntry

    public string Street  get; set; 
    public uint Number  get; set; 
    public string City  get; set; 

以及包含 ID 字段的 BaseEntry。

public abstract class BaseEntry

    [Key]
    public ulong Id  get; set; 

这是第一次确保表格内容的迁移

    protected override void Up(MigrationBuilder migrationBuilder)
    
        migrationBuilder.CreateTable(
            name: "Companies",
            columns: table => new
            
                Id = table.Column<ulong>(type: "INTEGER", nullable: false)
                    .Annotation("Sqlite:Autoincrement", true)
            ,
            constraints: table =>
            
                table.PrimaryKey("PK_Companies", x => x.Id);
            );

        migrationBuilder.CreateTable(
            name: "Person",
            columns: table => new
            
                Id = table.Column<ulong>(type: "INTEGER", nullable: false)
                    .Annotation("Sqlite:Autoincrement", true),
                Name = table.Column<string>(type: "TEXT", nullable: true),
                Firstname = table.Column<string>(type: "TEXT", nullable: true),
                CompanyId = table.Column<ulong>(type: "INTEGER", nullable: true)
            ,
            constraints: table =>
            
                table.PrimaryKey("PK_Person", x => x.Id);
                table.ForeignKey(
                    name: "FK_Person_Companies_CompanyId",
                    column: x => x.CompanyId,
                    principalTable: "Companies",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Restrict);
            );

        migrationBuilder.CreateIndex(
            name: "IX_Person_CompanyId",
            table: "Person",
            column: "CompanyId");
    

    protected override void Down(MigrationBuilder migrationBuilder)
    
        migrationBuilder.DropTable(
            name: "Person");

        migrationBuilder.DropTable(
            name: "Companies");
    

然后是第二次迁移,它将地址内容添加到数据库:

    protected override void Up(MigrationBuilder migrationBuilder)
    
        migrationBuilder.AddColumn<ulong>(
            name: "AddressId",
            table: "Person",
            type: "INTEGER",
            nullable: true);

        migrationBuilder.CreateTable(
            name: "Address",
            columns: table => new
            
                Id = table.Column<ulong>(type: "INTEGER", nullable: false)
                    .Annotation("Sqlite:Autoincrement", true),
                Street = table.Column<string>(type: "TEXT", nullable: true),
                Number = table.Column<uint>(type: "INTEGER", nullable: false),
                City = table.Column<string>(type: "TEXT", nullable: true)
            ,
            constraints: table =>
            
                table.PrimaryKey("PK_Address", x => x.Id);
            );

        migrationBuilder.CreateIndex(
            name: "IX_Person_AddressId",
            table: "Person",
            column: "AddressId");

        migrationBuilder.AddForeignKey(
            name: "FK_Person_Address_AddressId",
            table: "Person",
            column: "AddressId",
            principalTable: "Address",
            principalColumn: "Id",
            onDelete: ReferentialAction.Restrict);
    

    protected override void Down(MigrationBuilder migrationBuilder)
    
        migrationBuilder.DropForeignKey(
            name: "FK_Person_Address_AddressId",
            table: "Person");

        migrationBuilder.DropTable(
            name: "Address");

        migrationBuilder.DropIndex(
            name: "IX_Person_AddressId",
            table: "Person");

        migrationBuilder.DropColumn(
            name: "AddressId",
            table: "Person");
    

但是这个迁移永远不会到达,因为第一次迁移失败是因为 Table 已经存在。我的主要问题是,如何说 EFCore 数据库已经迁移到特定点并且它应该只从该点迁移?

【问题讨论】:

你先搭建脚手架了吗? docs.microsoft.com/en-us/ef/core/managing-schemas/… 【参考方案1】:

如果您想告诉 EF 您的第一次迁移已经迁移。您可以手动将其添加到迁移历史表默认表是__EFMigrationsHistory

MigrationId ProductVersion
2021061200000_initialize_blah_blah 5.0.1.2
your_migration_id version

再次进行迁移,它将跳过历史记录表中已经存在的迁移

如果您只是想迁移特定的迁移,您可以告诉它同时 你运行迁移命令

update-database -migration "your-migration"

【讨论】:

谢谢@朝仓游侠。如何以编程方式将此条目添加到此表中? 如果您的目的是迁移特定的迁移,那么您可以从 dbContext yourDbContext.GetService&lt;IMigrator&gt;().Migrate("your_migration"); 如果您想添加记录但不能直接访问数据库,您可以从控制台命令和代码运行原始 Sql 脚本。这个:***.com/questions/35184476/… 和这个:***.com/questions/14283082/…

以上是关于C# Entity Framework Core Migrations 最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

C# Entity Framework Core Migrations 最佳实践

如何将 C# 8.0 可空引用类型与 Entity Framework Core 模型一起使用?

System.Core 错误:使用 C# Entity Framework 和 Linq 和 Expression 的“代码应该无法访问”

在 C# 中使用 Entity Framework Core 插入数据之前检查重复字符串数据的最佳实践

带有 Entity Framework Core 的 SQLite 很慢

Entity Framework Core 2 中的 ConnectionString 生成器