.Net 对象中的 Powershell 事件顺序混乱

.Net 对象中的 Powershell 事件顺序混乱

我正在使用 Sql Server SMO 和 Powershell 脚本编写 SQL 脚本的执行。我们发现了两个问题:

  1. Powershell 的事件处理不按顺序进行
  2. 在所有事件被处理之前,控制权被返回给调用脚本(这很有意义,因为它们应该是异步的)。

鉴于此脚本:

    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))

这将有效地暂停脚本,直到作业完成。它还处理了您的作业从未创建的可能性(如果出现错误或其他类似原因)。由于您的完成消息行跟在此代码后面,因此您的完成消息应该位于日志的末尾。

相关内容