我正在使用 Sql Server SMO 和 Powershell 脚本编写 SQL 脚本的执行。我们发现了两个问题:
- Powershell 的事件处理不按顺序进行
- 在所有事件被处理之前,控制权被返回给调用脚本(这很有意义,因为它们应该是异步的)。
鉴于此脚本:
Add-Type -AssemblyName "Microsoft.SqlServer.SMO, Version=12.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" | Out-Null
Add-Type -AssemblyName "Microsoft.SqlServer.ConnectionInfo, Version=12.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" | Out-Null
(Get-Date -format "dd-MMM-yyyy HH:mm:ss.fff") + ": Starting Script" | Out-File -FilePath $sqlOutputFilename -Append
$serverConnection = New-Object ('Microsoft.SqlServer.Management.Common.ServerConnection') "FTS-DEVSERVER", "SA", "avid4uin)*"
$serverSMO = new-object ('Microsoft.SqlServer.Management.Smo.Server') $serverConnection
$sqlOutputFilename = "output.log"
$serverMessage = { (Get-Date -format "dd-MMM-yyyy HH:mm:ss.fff") + ": " + $EventArgs.Error.Message | Out-File -FilePath $event.MessageData -Append;}
Register-ObjectEvent -InputObject $serverSMO.ConnectionContext -EventName "ServerMessage" -Action $serverMessage -MessageData $sqlOutputFilename | Out-Null
$script = "
declare @counter int = 0
WHILE @Counter < 100
BEGIN
PRINT @Counter
SET @Counter = @Counter + 1
END
GO
"
$databaseSMO = $serverSMO.Databases["master"]
$databaseSMO.ExecuteNonQuery($script) | Out-Null
(Get-Date -format "dd-MMM-yyyy HH:mm:ss.fff") + ": We believe the script is completed" | Out-File -FilePath $sqlOutputFilename -Append
产生以下输出(为简洁起见,略去不表)
15-Nov-2016 16:03:29.501: Starting Script
15-Nov-2016 16:03:29.521: Changed database context to 'master'.
15-Nov-2016 16:03:29.523: 5
15-Nov-2016 16:03:29.528: 0
15-Nov-2016 16:03:29.529: 6
15-Nov-2016 16:03:29.530: 7
15-Nov-2016 16:03:29.531: 8
15-Nov-2016 16:03:29.530: We believe the script is completed
15-Nov-2016 16:03:29.532: 9
15-Nov-2016 16:03:29.534: 10
15-Nov-2016 16:03:29.537: 11
15-Nov-2016 16:03:29.539: 12
15-Nov-2016 16:03:29.548: 13
15-Nov-2016 16:03:29.549: 14
我们之前在本机 .Net 应用程序中使用过 SMO,但我记得没有见过这种行为。我们尝试在代码执行中添加休眠(在最终打印之前),除了减慢脚本速度(包括像仅在单个线程上一样处理事件)之外,它没有改变任何东西。
答案1
Register-ObjectEvent
在后台使用 PoSh 的作业系统来执行其功能。您的所有脚本代码都在作业完成之前完成。这就是为什么您会在意想不到的位置看到完成消息。
要解决此问题,您需要为调用分配一个变量Register-ObjectEvent
并删除到的管道Out-Null
。这将为您提供在后台运行的作业的句柄。
然后你需要用循环来监控你的工作进度。效果如下:
Do{
Start-Sleep -Seconds 1
$check = Get-Job -Id $RegisteredEvent.Id
}Until(($check.State -ne "Running") -or ($check -eq $null))
这将有效地暂停脚本,直到作业完成。它还处理了您的作业从未创建的可能性(如果出现错误或其他类似原因)。由于您的完成消息行跟在此代码后面,因此您的完成消息应该位于日志的末尾。