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

如何从多个不同NuGet包运行EF Core迁移?

多插件EF Core迁移实现方案

要实现主项目+多插件的迁移支持,核心是让EF Core能够扫描并加载多个程序集里的迁移文件,以下是具体实现步骤:

1. 自定义迁移程序集聚合类

创建CompositeMigrationsAssembly类,实现EF Core的IMigrationsAssembly接口,用于聚合主项目和所有插件的迁移:

using Microsoft.EntityFrameworkCore.Migrations;
using System.Reflection;

public class CompositeMigrationsAssembly : IMigrationsAssembly
{
    private readonly IMigrationsAssembly _innerAssembly;
    private readonly IEnumerable<Assembly> _additionalAssemblies;

    public CompositeMigrationsAssembly(
        IMigrationsAssembly innerAssembly,
        IEnumerable<Assembly> additionalAssemblies)
    {
        _innerAssembly = innerAssembly;
        _additionalAssemblies = additionalAssemblies;
    }

    public string MigrationsNamespace => _innerAssembly.MigrationsNamespace;

    public Assembly Assembly => _innerAssembly.Assembly;

    public IReadOnlyDictionary<string, Type> Migrations => GetAllMigrations();

    public Migration GetMigration(string migrationId)
    {
        var migration = _innerAssembly.Migrations.TryGetValue(migrationId, out var type) 
            ? (Migration)Activator.CreateInstance(type) 
            : null;

        if (migration == null)
        {
            foreach (var assembly in _additionalAssemblies)
            {
                var migrationsInAssembly = assembly.GetTypes()
                    .Where(t => typeof(Migration).IsAssignableFrom(t) && !t.IsAbstract)
                    .ToDictionary(t => t.GetCustomAttribute<MigrationAttribute>()!.Id);

                if (migrationsInAssembly.TryGetValue(migrationId, out var migrationType))
                {
                    migration = (Migration)Activator.CreateInstance(migrationType);
                    break;
                }
            }
        }

        return migration ?? throw new InvalidOperationException($"Migration {migrationId} not found.");
    }

    public Type GetMigrationType(string migrationId)
    {
        var type = _innerAssembly.Migrations.TryGetValue(migrationId, out var t) ? t : null;

        if (type == null)
        {
            foreach (var assembly in _additionalAssemblies)
            {
                var migrationsInAssembly = assembly.GetTypes()
                    .Where(t => typeof(Migration).IsAssignableFrom(t) && !t.IsAbstract)
                    .ToDictionary(t => t.GetCustomAttribute<MigrationAttribute>()!.Id);

                if (migrationsInAssembly.TryGetValue(migrationId, out var migrationType))
                {
                    type = migrationType;
                    break;
                }
            }
        }

        return type ?? throw new InvalidOperationException($"Migration type {migrationId} not found.");
    }

    private IReadOnlyDictionary<string, Type> GetAllMigrations()
    {
        var allMigrations = new Dictionary<string, Type>(_innerAssembly.Migrations);

        foreach (var assembly in _additionalAssemblies)
        {
            var migrationsInAssembly = assembly.GetTypes()
                .Where(t => typeof(Migration).IsAssignableFrom(t) && !t.IsAbstract)
                .Select(t => new { Id = t.GetCustomAttribute<MigrationAttribute>()!.Id, Type = t });

            foreach (var migration in migrationsInAssembly)
            {
                if (!allMigrations.ContainsKey(migration.Id))
                {
                    allMigrations.Add(migration.Id, migration.Type);
                }
            }
        }

        return allMigrations;
    }
}

2. 配置DbContext并替换迁移程序集服务

在Program.cs(或Startup.cs)中,先正常注册DbContext,再替换默认的IMigrationsAssembly为自定义的聚合实现:

// 收集所有需要包含的迁移程序集:主项目程序集 + 所有插件程序集
var migrationAssemblies = new List<Assembly>
{
    typeof(MyDbContext).Assembly, // 主项目的程序集
    // 添加已加载的插件程序集,比如你启动时获取到的pluginAssemblies集合
    ...pluginAssemblies
};

// 注册DbContext,无需指定单个MigrationsAssembly
builder.Services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// 替换IMigrationsAssembly服务,实现多程序集迁移加载
builder.Services.AddScoped<IMigrationsAssembly>(sp =>
{
    var defaultMigrationsAssembly = sp.GetRequiredService<IMigrationsAssembly>();
    // 传入主项目之外的其他迁移程序集(插件程序集)
    return new CompositeMigrationsAssembly(defaultMigrationsAssembly, migrationAssemblies.Skip(1));
});

3. 生成各模块的迁移

使用EF Core CLI分别为主项目和每个插件生成迁移:

  • 生成主项目迁移:
dotnet ef migrations add AddMainProjectEntities --project ./MainProject --startup-project ./MainProject
  • 生成某插件的迁移(以PluginA为例):
dotnet ef migrations add AddPluginAEntities --project ./Plugins/PluginA --startup-project ./MainProject

4. 运行所有迁移

直接在主项目执行数据库更新命令,自定义的CompositeMigrationsAssembly会自动加载所有程序集的迁移并执行:

dotnet ef database update --startup-project ./MainProject

注意事项

  • 确保所有迁移的ID唯一:EF Core自动生成的迁移ID包含时间戳,只要不手动修改,不会出现重复问题。
  • 插件项目需引用EF Core相关包:确保插件项目能正常生成迁移文件。
  • 设计时访问DbContext:生成插件迁移时,需通过--startup-project指定主项目,确保EF Core能找到DbContext的配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 12:37:49