.Net对象的PowerShell事件不按顺序

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

  1. Powershell的事件处理按顺序处理
  2. 在所有事件都被处理之前,控制权被返回到调用脚本(这是有道理的,因为它们应该是asynchronous的)。

鉴于这个脚本:

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,并没有看到我能记得的这种行为。 我们已经尝试在代码的执行中添加睡眠(在最终打印之前),除了减慢脚本的速度外,它不会改变任何东西,包括处理事件,比如它只在单个线程上。

Register-ObjectEvent使用PoSh的作业系统来执行其function。 所有脚本的代码都在作业完成之前完成。 这就是为什么你看到你的完成消息在一个意想不到的位置。

要解决这个问题,你需要给你的Register-ObjectEvent调用分配一个variables,并把你的pipe道移出Out-Null 。 这会给你一个处理在后台运行的工作。

然后你需要用一个循环监视你的工作进度。 一些事情的影响:

 Do{ Start-Sleep -Seconds 1 $check = Get-Job -Id $RegisteredEvent.Id }Until(($check.State -ne "Running") -or ($check -eq $null)) 

这将有效地暂停脚本,直到作业完成。 它也处理了你的工作从来没有创build(如出现错误或其他原因)的可能性。 由于您的完成消息行跟踪此代码,您的完成消息应该在日志的末尾。