代码优先迁移-更新数据库-脚本命令生成的SQL脚本不起作用



我必须通过实体框架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中手动执行上面的查询,当时它显示了以下错误。

消息207,级别16,状态1,第2行
列名"Abstract"无效。

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

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

-Verbose输出仅显示语句摘要。如果在SQLServerManagementStudio中手动运行命令,则需要在以下两条语句之间使用GO

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

快速解决方法是做一些类似的事情:

  • 更新数据库-脚本
  • 然后使用Sql Server management Studio在生成的脚本中进行以下搜索和替换:
    • 查找内容:^{:b*}{{INSERT|UPDATE|SELECT|DELETE}.+}这将查找任何CRUD语句
    • 替换为:1GOn12n保留缩进,并在任何CRUD语句之前添加GO
    • 查找选项:使用正则表达式

但是,请注意,-Verbose并没有为您提供所需的输出,您需要-Script的输出,否则您将丢失为__MigrationHistory历史表插入的数据,这可能会导致应用程序在运行时抛出错误(有关详细信息,请参阅下文)。

详细信息

您在下面对MSDN代码优先迁移页面上的信息发表的评论很有意思。页面实际上说明(在"获取SQL脚本"部分下)

运行"更新数据库"命令,但这次指定–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如何知道它使用的是最新的数据库版本(因此将运行而不是向您显示错误)。但是,它仍然缺少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

如果您因为生成大型脚本而感到沮丧,那么在任何ALTER TABLE行的末尾放置一个GO对于SSMS中的replace来说都是微不足道的,它就像这个答案

顶部的那个一样

最新更新