如何从多个不同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
相关产品推荐
相关产品推荐

