无需CLI:为Entity Framework Core配置自定义迁移文件夹
自定义EF Core迁移文件输出路径的实现方案
EF Core迁移命令依赖设计时注入的IMigrationsScaffolder服务,要实现自动按年月分文件夹管理迁移文件,需通过装饰器模式包装默认服务,并通过设计时服务配置注册自定义实现,而非直接修改运行时服务。
1. 安装依赖包
确保项目已安装Microsoft.EntityFrameworkCore.Design包:
dotnet add package Microsoft.EntityFrameworkCore.Design
2. 实现自定义迁移脚手架装饰器
创建类包装默认的MigrationsScaffolder,动态生成按年月划分的输出路径:
using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Migrations.Design; using Microsoft.EntityFrameworkCore.Migrations.Operations; using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; public class DateTimePartitionedMigrationsScaffolder : IMigrationsScaffolder { private readonly IMigrationsScaffolder _innerScaffolder; private readonly ILogger<DateTimePartitionedMigrationsScaffolder> _logger; public DateTimePartitionedMigrationsScaffolder(IMigrationsScaffolder innerScaffolder, ILogger<DateTimePartitionedMigrationsScaffolder> logger) { _innerScaffolder = innerScaffolder; _logger = logger; } public async Task<ScaffoldedMigration> ScaffoldMigrationAsync(string migrationName, string rootNamespace, string subNamespace = null, string language = null) { var now = DateTime.Now; // 生成年月格式的子路径和命名空间 var targetSubNamespace = $"Data.Migrations.{now.Year}.{now.Month:D2}"; var targetDir = $"Data/Migrations/{now.Year}/{now.Month:D2}"; _logger.LogInformation($"迁移文件将输出至: {targetDir}"); // 调用默认脚手架逻辑,传入自定义命名空间 var scaffoldedMigration = await _innerScaffolder.ScaffoldMigrationAsync( migrationName, rootNamespace, targetSubNamespace, language); // 修正文件输出路径,确保文件生成到目标文件夹 scaffoldedMigration.MigrationFile.Path = Path.Combine(targetDir, Path.GetFileName(scaffoldedMigration.MigrationFile.Path)); scaffoldedMigration.MetadataFile.Path = Path.Combine(targetDir, Path.GetFileName(scaffoldedMigration.MetadataFile.Path)); return scaffoldedMigration; } // 其余接口方法直接委托给默认实现 public Task DeleteMigrationAsync(string projectDir, string rootNamespace, bool force, string language = null) => _innerScaffolder.DeleteMigrationAsync(projectDir, rootNamespace, force, language); public Task<IReadOnlyList<string>> GetMigrationNamesAsync(string projectDir, string rootNamespace, string language = null) => _innerScaffolder.GetMigrationNamesAsync(projectDir, rootNamespace, language); public Task<string> GetMigrationIdAsync(string migrationName, string projectDir, string rootNamespace, string language = null) => _innerScaffolder.GetMigrationIdAsync(migrationName, projectDir, rootNamespace, language); }
3. 注册设计时服务
创建实现IDesignTimeServices的类,替换默认的迁移脚手架服务:
using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Migrations.Design; using Microsoft.Extensions.DependencyInjection; public class CustomDesignTimeServices : IDesignTimeServices { public void ConfigureDesignTimeServices(IServiceCollection services) { services.AddScoped<IMigrationsScaffolder>(sp => { var innerScaffolder = sp.GetRequiredService<MigrationsScaffolder>(); var logger = sp.GetRequiredService<ILogger<DateTimePartitionedMigrationsScaffolder>>(); return new DateTimePartitionedMigrationsScaffolder(innerScaffolder, logger); }); } }
4. 验证效果
执行迁移命令:
# PowerShell命令 Add-Migration TestMigration # 或DotNet CLI命令 dotnet ef migrations add TestMigration
控制台会输出路径日志,迁移文件将自动生成到Data/Migrations/YYYY/MM/目录下,命名空间同步调整为项目根命名空间.Data.Migrations.YYYY.MM。
注意事项
CustomDesignTimeServices需与DbContext处于同一项目,确保EF Core设计时工具能扫描到- 移除迁移时(
Remove-Migration或dotnet ef migrations remove),默认服务会自动根据迁移ID定位到对应文件夹的文件,无需额外修改 - .NET 6+顶级语句Program.cs无需额外配置,设计时服务会被自动加载
内容的提问来源于stack exchange,提问作者Steffen Cole Blake
相关产品推荐
相关产品推荐

