You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mac上Visual Studio中EF7 Code First迁移异常问题

问题描述

我近期更换为Mac M1设备,安装了Visual Studio与.NET 7,通过Docker容器运行SQL Server,正在开发ASP.NET Core 7 MVC Web应用。

删除SQL Server中的数据库与本地迁移文件夹后,通过NuGet包管理器控制台执行:

Add-Migration Initial -Context DB

迁移文件夹成功生成,包含Initial迁移类。但执行Update-Database命令后,仅创建了__EFMigrationsHistory表,其余业务表均未生成。

若再执行:

Add-Migration SecondMigration -Context DB

并执行更新命令,所有业务表会被创建,但会抛出部分表已存在的错误。

添加第三次迁移并更新数据库时,EF会尝试再次执行第一次迁移,查看__EFMigrationsHistory表可见migrationID仍为第一次迁移的ID,似乎每次执行update-database都会使用错误的迁移版本。

我想知道是否有相关配置可设置,或是需要在执行Update-Database命令时显式指定迁移版本?令我困惑的是,Initial迁移类中明明包含所有业务表的创建代码,为何仅生成了__EFMigrationsHistory表。


相关代码

DB上下文类

public class DB : DbContext
{
        public DB(DbContextOptions<DB> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasDefaultSchema("dbo");
            modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());

            modelBuilder.Entity<User>()
                .HasQueryFilter(a=>a.Id != 1);

            foreach (var foreignKey in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
            {
                foreignKey.DeleteBehavior = DeleteBehavior.NoAction;
            }
        }

        public  DbSet<ApiRequest> ApiRequest { get; set; }
        public  DbSet<EmailLog> EmailLog { get; set; }
        public  DbSet<NotificationDelivery> NotificationDelivery { get; set; }
        public  DbSet<EmailTemplate> EmailTemplate { get; set; }
        public  DbSet<ErrorLog> ErrorLog { get; set; }
        public  DbSet<GeoLocation> GeoLocation { get; set; }
}

实体类示例

[Table("ApiRequest")]
public class ApiRequest
{
    public long Id { get; set; }
    public string Name { get; set; }
    public DateTime LastUpdated { get; set; }
    public bool Active { get; set; }
}

实体配置示例

public class ApiRequestConfig : IEntityTypeConfiguration<ApiRequest>
{
    public void Configure(EntityTypeBuilder<ApiRequest> modelBuilder)
    {
        modelBuilder
            .HasKey(x => x.Id);

        modelBuilder
            .Property(x => x.Id)
            .ValueGeneratedOnAdd();

        modelBuilder
            .Property(x => x.Name)
            .IsRequired()
            .HasMaxLength(50);

        modelBuilder
            .Property(x => x.LastUpdated)
            .IsRequired()
            .HasColumnType("datetime2(0)");

        modelBuilder
            .Property(x => x.Active)
            .IsRequired()
            .HasDefaultValue(true);
    }
}

Program.cs配置

builder.Services.AddDbContext<DB>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("ClientDatabase"));
});

迁移类示例

public partial class Initial : Migration
{
    /// <inheritdoc />
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.EnsureSchema(
            name: "dbo");

        migrationBuilder.CreateTable(
            name: "ApiRequest",
            schema: "dbo",
            columns: table => new
            {
                Id = table.Column<long>(type: "bigint", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
                LastUpdated = table.Column<DateTime>(type: "datetime2(0)", nullable: false),
                Active = table.Column<bool>(type: "bit", nullable: false, defaultValue: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_ApiRequest", x => x.Id);
            });
     }

      migrationBuilder.CreateTable(
            name: "EmailTemplate",
            schema: "dbo",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Name = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
                Description = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
                Template = table.Column<string>(type: "nvarchar(max)", nullable: false),
                Placeholders = table.Column<string>(type: "nvarchar(512)", maxLength: 512, nullable: true),
                Active = table.Column<bool>(type: "bit", nullable: false, defaultValue: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_EmailTemplate", x => x.Id);
            });
 }

解决思路与方案

1. 排查Initial迁移未执行的核心原因

Initial迁移类虽包含建表代码但未执行,大概率是迁移过程中出现了静默错误(EF未抛出异常),可通过以下方式排查:

  • 执行更新命令时开启详细日志,查看执行细节:
    Update-Database -Context DB -Verbose
    
  • 检查SQL Server容器的日志,确认是否有数据库层面的权限、Schema错误。

2. 彻底重置迁移与数据库

当前__EFMigrationsHistory表记录与实际数据库状态不匹配,按以下步骤重置:

  1. 删除SQL Server中的目标数据库,同时删除本地所有迁移文件夹。
  2. 重新生成干净的初始迁移:
    Add-Migration Initial -Context DB -Force
    
  3. 显式指定迁移版本执行更新:
    Update-Database Initial -Context DB
    

3. 修复上下文配置的潜在问题

  • 上下文的OnModelCreating中引用了User实体,但未声明DbSet<User>,这可能导致查询过滤器抛出异常中断迁移,建议注释或补充DbSet<User>后重新测试。
  • 确认ApplyConfigurationsFromAssembly是否正确加载了所有实体配置类,避免因配置遗漏导致EF无法识别实体。

4. 规范后续迁移操作

  • 每次重置迁移后,确保__EFMigrationsHistory表与本地迁移文件完全同步。
  • 若遇到版本匹配问题,执行Update-Database时显式指定迁移名称(如Update-Database SecondMigration -Context DB)。
  • 开发环境中避免手动修改迁移文件或数据库结构,保持EF迁移为唯一数据源。

内容的提问来源于stack exchange,提问作者Confused Guy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 04:01:19