ABP框架模块化应用中EF Core Code-First存储过程迁移实现
在ABP框架模块化应用中创建存储过程的正确做法
方法1:利用模块的SQL脚本文件自动执行
- 在你的模块项目里(推荐放在
EntityFrameworkCore项目下)新建Sql文件夹,再按数据库类型建子目录,比如SqlServer、MySQL,把存储过程脚本放到对应目录里,命名建议带上模块前缀,比如MyModule_GetUserOrders.sql。 - 打开模块的核心类(继承
AbpModule的那个类),重写ConfigureServices方法,配置让框架自动加载这些脚本:
public override void ConfigureServices(ServiceConfigurationContext context) { Configure<AbpDbContextOptions>(options => { options.UseSqlServer(); // 根据你的数据库类型调整 options.Configure(ctx => { // 替换成你脚本所在的程序集和命名空间 ctx.Context.ModelBuilder.AddSqlScriptFiles( typeof(MyModule).Assembly, "YourModuleNamespace.EntityFrameworkCore.Sql.SqlServer" ); }); }); }
- 当最终应用启动并执行数据库迁移/初始化时,这些脚本会被自动执行,存储过程就创建好了。
方法2:结合迁移脚本处理
- 在模块的
EntityFrameworkCore项目里创建空迁移,然后在迁移类的Up方法里读取并执行存储过程脚本,Down方法里写删除逻辑:
public partial class AddGetUserOrdersProc : Migration { protected override void Up(MigrationBuilder migrationBuilder) { // 读取脚本文件,注意路径要和文件输出目录匹配 var scriptPath = Path.Combine(AppContext.BaseDirectory, "Sql/SqlServer/MyModule_GetUserOrders.sql"); var procScript = File.ReadAllText(scriptPath); migrationBuilder.Sql(procScript); } protected override void Down(MigrationBuilder migrationBuilder) { // 不同数据库的删除语法不同,这里以SQL Server为例 migrationBuilder.Sql("DROP PROCEDURE IF EXISTS MyModule_GetUserOrders"); } }
- 记得把脚本文件的「复制到输出目录」属性改成「始终复制」,不然最终应用找不到脚本文件。
- 当最终应用合并所有模块的迁移时,这个迁移会被执行,自动创建存储过程。
方法3:通过数据种子(Data Seeding)执行
- 如果你的存储过程不需要在迁移阶段执行,而是在应用首次启动或数据重置时创建,可以实现
IDataSeedContributor接口:
public class MyModuleProcSeeder : IDataSeedContributor, ITransientDependency { private readonly IServiceProvider _serviceProvider; public MyModuleProcSeeder(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task SeedAsync(DataSeedContext context) { using var scope = _serviceProvider.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<MyModuleDbContext>(); var scriptPath = Path.Combine(AppContext.BaseDirectory, "Sql/SqlServer/MyModule_GetUserOrders.sql"); var procScript = await File.ReadAllTextAsync(scriptPath); // 执行脚本,注意加判断避免重复创建 await dbContext.Database.ExecuteSqlRawAsync(procScript); } }
- 这种方式要在脚本里加
IF NOT EXISTS判断,比如SQL Server可以写:IF NOT EXISTS (SELECT * FROM sys.procedures WHERE name = 'MyModule_GetUserOrders') BEGIN ... END,防止重复执行报错。
要注意的细节
- 不同数据库的存储过程语法不一样,一定要按数据库类型分目录管理脚本。
- 脚本文件的输出路径要配置正确,不然最终应用运行时会找不到文件。
- 如果模块有依赖关系,要确保依赖模块的存储过程先被创建,避免出现引用错误。
内容的提问来源于stack exchange,提问作者Mehdi JM
相关产品推荐
相关产品推荐

