作为规划实体框架迁移的一部分,为了调试数据移动,我经常使用-Script参数来生成脚本。
然后,我可以将此脚本带到查询分析器并将其包装在事务中,以便手动测试它。
我遇到了一个需要Go语句才能正确执行脚本的情况。以下代码已添加到迁移中,以便在适当的位置输出Go。
Sql("GO");
当使用-Script时,这会在正确的位置添加GO语句。但是当没有使用-Script时。我得到例外......
System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure 'GO'.
是否有一种安全的方法可以将Go命令添加到脚本中?
答案 0 :(得分:14)
我最近遇到了完全相同的情况。我的EF代码迁移通常会引入新的表或列,然后我还使用Sql(...)将数据迁移放入有时想要引用新表/列的迁移中。正如您所指出的,当作为EF代码迁移运行时,每个语句似乎都作为离散批处理发布到DB,因此没有任何问题。但是,为了满足生产部署限制,我们将一组代码迁移从sprint转换为单个脚本(使用-Script),以便为部署团队提供单个聚合SQL脚本迁移。正如您所指出的,这个脚本文件有时会失败,因为它试图从单个代码迁移中处理单个T SQL批处理,后面的语句试图引用仅在批处理中先前定义的结构。
我并不特别喜欢我现在采取的两种方法中的任何一种来减轻这种情况,但在这里它们是:
一个。如果我当时正在考虑这个问题,我会将代码迁移分成两个迁移,这样当它们编写脚本时,它们会分成两个(或更多个)单独的批处理。我不喜欢这样,因为在代码迁移的开发过程中没有反馈这是必要的,因此它似乎容易出错。
湾当我生成聚合脚本时,我会在临时数据库上运行它们来证明它们,并且我最终在该脚本中必要时手动注入“GO”语句。这是一个令人讨厌的过程,必须返回并执行,并导致-Script输出不是代码迁移的100%反映。
我没有花太多时间深入研究EF代码迁移的源代码,看看我是否能理解为什么它将“GO”解释为存储过程,以及源代码中是否有任何指向一种提供避免这种情况的指令的方法。
答案 1 :(得分:8)
internal sealed class Configuration : DbMigrationsConfiguration<Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
const string providerInvariantName = "System.Data.SqlClient";
SetSqlGenerator(providerInvariantName, new BatchingMigrationSqlGenerator(GetSqlGenerator(providerInvariantName)));
}
protected override void Seed(Context context)
{
}
}
internal class BatchingMigrationSqlGenerator : MigrationSqlGenerator
{
private readonly MigrationSqlGenerator migrationSqlGenerator;
public BatchingMigrationSqlGenerator(MigrationSqlGenerator migrationSqlGenerator)
{
this.migrationSqlGenerator = migrationSqlGenerator;
}
public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
{
var migrationStatements = migrationSqlGenerator.Generate(migrationOperations, providerManifestToken).ToArray();
foreach (var migrationStatement in migrationStatements)
{
migrationStatement.BatchTerminator = "GO";
}
return migrationStatements;
}
}
答案 2 :(得分:2)
当我使用和不使用Configuration
参数运行迁移时,我最终使用了两个不同的-Script
类。在我的一个Configuration
类中,我将其MigrationSqlGenerator
包装在自定义实现中,该实现添加了GO
语句。
答案 3 :(得分:0)
我已经使用过:
public class MigrationScriptBuilder : SqlServerMigrationSqlGenerator
{
#if !DEBUG
protected override void Generate(System.Data.Entity.Migrations.Model.SqlOperation sqlOperation)
{
Statement("GO");
base.Generate(sqlOperation);
Statement("GO");
}
#endif
}
因此,当它进行调试时,它不会崩溃。我从发布模式编写脚本。
答案 4 :(得分:0)
这对我有用:
public class MigrationScriptBuilder : SqlServerMigrationSqlGenerator
{
public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
{
var statements = base.Generate(migrationOperations, providerManifestToken);
statements = statements.SelectMany(s => new[] {
s,
new MigrationStatement
{
Sql = "GO"
}
}).ToList();
return statements;
}
}
在DbContext配置中使用这种方法可以使用(在其他答案中看到)(迁移过程流程):
public Configuration()
{
SetSqlGenerator("System.Data.SqlClient", new MigrationScriptBuilder());
}