因此,通过大量使用ILSpy 和the answer to this question 中的一些指针,我找到了一种方法。
下面有兴趣的人的详细信息。
问题
SqlServerMigrationSqlGenerator 是最终负责创建 SQL 语句的类,这些语句在使用包管理器控制台中的 -Script 开关或使用 MigratorScriptingDecorator 时对目标数据库执行或脚本化。
工作
检查SqlServerMigrationSqlGenerator 中负责DROP COLUMN 的Genearate 方法,它看起来像这样:
protected virtual void Generate(DropColumnOperation dropColumnOperation)
{
RuntimeFailureMethods
.Requires(dropColumnOperation != null, null, "dropColumnOperation != null");
using (IndentedTextWriter indentedTextWriter =
SqlServerMigrationSqlGenerator.Writer())
{
string value = "@var" + this._variableCounter++;
indentedTextWriter.Write("DECLARE ");
indentedTextWriter.Write(value);
indentedTextWriter.WriteLine(" nvarchar(128)");
indentedTextWriter.Write("SELECT ");
indentedTextWriter.Write(value);
indentedTextWriter.WriteLine(" = name");
indentedTextWriter.WriteLine("FROM sys.default_constraints");
indentedTextWriter.Write("WHERE parent_object_id = object_id(N'");
indentedTextWriter.Write(dropColumnOperation.Table);
indentedTextWriter.WriteLine("')");
indentedTextWriter.Write("AND col_name(parent_object_id,
parent_column_id) = '");
indentedTextWriter.Write(dropColumnOperation.Name);
indentedTextWriter.WriteLine("';");
indentedTextWriter.Write("IF ");
indentedTextWriter.Write(value);
indentedTextWriter.WriteLine(" IS NOT NULL");
indentedTextWriter.Indent++;
indentedTextWriter.Write("EXECUTE('ALTER TABLE ");
indentedTextWriter.Write(this.Name(dropColumnOperation.Table));
indentedTextWriter.Write(" DROP CONSTRAINT ' + ");
indentedTextWriter.Write(value);
indentedTextWriter.WriteLine(")");
indentedTextWriter.Indent--;
indentedTextWriter.Write("ALTER TABLE ");
indentedTextWriter.Write(this.Name(dropColumnOperation.Table));
indentedTextWriter.Write(" DROP COLUMN ");
indentedTextWriter.Write(this.Quote(dropColumnOperation.Name));
this.Statement(indentedTextWriter);
}
}
您可以看到它跟踪使用的变量名称,但这似乎只在一个批次内跟踪,即单个迁移。因此,如果一个迁移包含多个DROP COLUM,上述工作正常,但如果有两个迁移导致生成DROP COLUMN,则_variableCounter 变量将被重置。
不生成脚本时不会遇到任何问题,因为每个语句都会立即针对数据库执行(我使用 SQL Profiler 检查过)。
如果您生成一个 SQL 脚本并想按原样运行它,尽管您遇到了问题。
解决方案
我创建了一个新的BatchSqlServerMigrationSqlGenerator,继承自SqlServerMigrationSqlGenerator,如下所示(注意你需要using System.Data.Entity.Migrations.Sql;):
public class BatchSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate
(System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
{
base.Generate(dropColumnOperation);
Statement("GO");
}
}
现在要强制迁移使用您的自定义生成器,您有两个选择:
-
如果您希望将其集成到包管理器控制台中,请将以下行添加到您的 Configuration 类中:
SetSqlGenerator("System.Data.SqlClient",
new BatchSqlServerMigrationSqlGenerator());
-
如果您是从代码生成脚本(就像我一样),请在您的代码中配置程序集的位置添加类似的代码行:
migrationsConfiguration.SetSqlGenerator(DataProviderInvariantName,
new BatchSqlServerMigrationSqlGenerator());