代码优先迁移 - Update-database -script命令生成的SQL脚本无法正常工作

时间:2013-08-30 08:32:24

标签: entity-framework entity-framework-5 ef-migrations

我必须通过Entity Framework 5使用以下模型创建数据库:

public class Post
{
        public int PostId { get; set; }
        [MaxLength(200)]
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
}

然后我在Post

中添加了新属性
public string Abstract { get; set; }

然后我跑了

Add-Migration AddPostAbstract

在我的Migrations文件夹中创建了以下类,之后我通过添加一个SQL语句修改了这个文件

//201308300714477_AddPostAbstract.cs
public override void Up()
{
                AddColumn("dbo.Posts", "Abstract", c => c.String());
                Sql("UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}

public override void Down()
{
    DropColumn("dbo.Posts", "Abstract");
}

之后我执行了这个命令

Update-Database –Verbose

更新了我的数据库并返回了这个SQL查询:

ALTER TABLE [dbo].[Posts] ADD [Abstract] [nvarchar](max)
UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL

现在我已经从表中删除了Abstract列,并尝试在SQL中手动执行上述查询,此时它会向我显示以下错误。

  

Msg 207,Level 16,State 1,Line 2
  列名称无效'摘要'。

我的问题是为什么在SQL中不执行此查询,即使此查询是通过迁移生成的?

或者有没有办法通过迁移生成的脚本运行这样的多个查询。

1 个答案:

答案 0 :(得分:12)

-Verbose输出只显示语句摘要。如果在SQL Server Management Studio中手动运行命令,那么在两个语句之间需要GO

ALTER TABLE [dbo].[Posts] ADD [Abstract] [nvarchar](max)
GO
UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL

快速解决方法是执行以下操作:

  • update-database -script
  • 然后使用Sql Server management Studio在生成的脚本中执行以下搜索和替换:
    • 找到:^{:b*}{{INSERT|UPDATE|SELECT|DELETE}.+}这可以找到任何CRUD语句
    • 替换为:\1GO\n\1\2\n保留缩进,并在任何CRUD语句之前添加GO
    • 查找选项:使用正则表达式

但是,请注意-Verbose没有为您提供所需的输出,您需要-Script的输出,否则您将缺少__MigrationHistory历史记录表的插入数据这可能会导致应用程序在运行时抛出错误(详见下文)。

<强>详情

以下关于MSDN Code First Migrations page信息的评论很有意思。该页面实际指出(在“获取SQL脚本”部分

下)
  

运行Update-Database命令,但这次指定-Script标志

如果你这样做,你会看到类似的东西:

ALTER TABLE [dbo].[Posts] ADD [Abstract] [nvarchar](max)
UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL
INSERT INTO [__MigrationHistory] ([MigrationId], [Model], [ProductVersion]) VALUES ( ...

INSERT非常重要 - 这就是您的应用程序中的EF将如何知道它使用最新的db版本(因此将运行而不是向您显示错误)。但是,仍然缺少GO命令。因此,SQL Server尝试将3行编译为单个批处理并失败。

在添加了您需要的GO语句后,您仍然可以通过以下方式在单个事务中运行此语句:

BEGIN TRANSACTION;
BEGIN TRY
   --Your migration code, with GO statements
END TRY
BEGIN CATCH
  SELECT 
    ERROR_NUMBER() AS ErrorNumber
    ,ERROR_SEVERITY() AS ErrorSeverity
    ,ERROR_STATE() AS ErrorState
    ,ERROR_PROCEDURE() AS ErrorProcedure
    ,ERROR_LINE() AS ErrorLine
    ,ERROR_MESSAGE() AS ErrorMessage;

  IF @@TRANCOUNT > 0
    ROLLBACK TRANSACTION;
END CATCH;

IF @@TRANCOUNT > 0
  COMMIT TRANSACTION;
GO

如果您因为生成大型脚本而感到沮丧,那么在GO行的末尾添加ALTER TABLE对于SSMS中的替换是微不足道的,这类似于顶部的那个这个答案