我必须通过实体框架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来说都是微不足道的,它就像这个答案