如何在C#中条件执行Fluent Migrator脚本?区分桌面与服务迁移表
刚好我之前处理过类似的场景,结合你已经用到的[Tags]属性,分两种情况给你梳理解决方案:
场景1:同一文件内拆分不同迁移类(推荐方案)
如果只是想把桌面和服务负责的表创建逻辑放在同一个文件里,最稳妥的方式是拆分两个独立的迁移类,分别标记对应标签:
比如在M01_CreateTables.cs文件中:
// 桌面程序负责创建的表,标记desktop标签 [Tags("desktop")] [Migration(101)] public class M01_CreateDesktopTables : ForwardOnlyMigration { public override void Up() { // 创建桌面专属表,比如配置表 Create.Table("AppConfig") .WithColumn("Id").AsInt32().PrimaryKey().Identity() .WithColumn("ConfigKey").AsString(50).NotNullable() .WithColumn("ConfigValue").AsString(200).NotNullable(); } } // Windows服务负责创建的表,标记service标签 [Tags("service")] [Migration(102)] public class M02_CreateServiceTables : ForwardOnlyMigration { public override void Up() { // 创建服务专属表,比如任务日志表 Create.Table("TaskLog") .WithColumn("Id").AsInt32().PrimaryKey().Identity() .WithColumn("TaskName").AsString(100).NotNullable() .WithColumn("LogContent").AsString(2000).NotNullable(); } }
接下来在桌面程序和服务中分别指定要执行的标签:
桌面程序端执行迁移:
// 初始化Fluent Migrator Runner(根据你的数据库类型调整,比如SqlServer) var runner = new RunnerBuilder() .AddSqlServer() .WithGlobalConnectionString("你的数据库连接字符串") .Build(); // 只运行标记了desktop标签的迁移 runner.MigrateUp(new MigrationOptions { Tags = new[] { "desktop" } });Windows服务端执行迁移:
var runner = new RunnerBuilder() .AddSqlServer() .WithGlobalConnectionString("你的数据库连接字符串") .Build(); // 只运行标记了service标签的迁移 runner.MigrateUp(new MigrationOptions { Tags = new[] { "service" } });
这种方式的好处是桌面和服务的迁移逻辑完全独立,Fluent Migrator的版本记录表(默认__MigrationHistory)会分别追踪各自的迁移状态,不会出现重复执行或遗漏的问题。
场景2:同一迁移类内区分执行逻辑(不推荐)
如果确实要在同一个Migration类里拆分桌面和服务的操作,可以通过自定义上下文参数来判断执行环境,但要注意迁移版本的唯一性问题:
首先在初始化Runner时传入执行环境标记:
桌面程序端:
var migrationContext = new MigrationContext(); migrationContext.Items["ExecutionEnv"] = "desktop"; var runner = new RunnerBuilder() .AddSqlServer() .WithGlobalConnectionString("你的连接字符串") .WithMigrationContext(migrationContext) .Build(); runner.MigrateUp();服务端同理,把
ExecutionEnv设为service。
然后在迁移类的Up方法里做条件判断:
[Migration(103)] public class M03_MixedMigration : ForwardOnlyMigration { public override void Up() { var executionEnv = Context.Items["ExecutionEnv"] as string; // 仅桌面程序执行的表创建 if (executionEnv == "desktop") { Create.Table("UserSetting") .WithColumn("Id").AsInt32().PrimaryKey().Identity() .WithColumn("UserId").AsInt32().NotNullable() .WithColumn("SettingValue").AsString(100).NotNullable(); } // 仅服务执行的表创建 if (executionEnv == "service") { Create.Table("JobQueue") .WithColumn("Id").AsInt32().PrimaryKey().Identity() .WithColumn("JobType").AsString(50).NotNullable() .WithColumn("JobData").AsString(500).NotNullable(); } } }
⚠️ 注意:这种方式有个坑——Fluent Migrator是按迁移版本号来标记执行状态的。桌面执行完这个版本的迁移后,服务再运行时会发现该版本已经被标记为已执行,不会再进入Up方法,导致服务需要创建的表不会被创建。所以除非你有特殊需求,否则优先用第一种标签拆分迁移类的方案。
额外提示
你原来代码里的[Tags("service")] [Tags("config")]是给迁移类标记多个标签,执行时只要匹配其中一个标签就会运行。如果需要精确区分,建议给桌面和服务分别用唯一标签(比如desktop和service),避免交叉触发。
内容的提问来源于stack exchange,提问作者user2496448

