问题
I am trying to pass a pre-built SmoServer object to a background job, to parallelize some operations against multiple SQL Servers. However, when I try to do this, the Child job of the invoked job gets stuck in a "NotStarted" state. A very basic test:
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO")
$SmoServer = New-Object Microsoft.SqlServer.Management.Smo.Server MySqlServer
Start-Job -Name Test -ScriptBlock {
param($SmoServer)
$SmoServer.Databases.Name
} -InitializationScript {
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO"); Import-Module SQLPS -DisableNameChecking
} -ArgumentList $SmoServer
The job starts, but the ChildJob gets stuck "NotStarted"
PS C:\Users\omrsafetyo> Get-Job Test
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
5 Test BackgroundJob Running True localhost param($SmoServer) $Smo...
PS C:\Users\omrsafetyo> Get-Job Test | select -expand childjobs
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
6 Job6 NotStarted True localhost param($SmoServer) $Smo...
I had encountered this a while ago, and never found a solution. And then I came across -IntializationScript, and thought that might be the silver bullet. It doesn't seem it is.
This same behavior is true with Invoke-Command. If I just run Invoke-Command, the command works fine. However, if I run Invoke-Command -AsJob, and pass an SmoServer object, it still fails.
How do I pass these complex objects that need an assembly/module loaded up front in the ArgumentList to a background job?
回答1:
PowerShell jobs are run in a separate process, and objects passed as arguments are serialized (via something like Export-CliXml
or the moral equivalent). When your object is "rehydrated" in the background process, it will not be an instance of Microsoft.SqlServer.Management.Smo.Server
anymore, but rather just an object that looks like one (it will have the same properties, as they were serialized in the originating process).
You can pass .NET objects to different runspaces within the same process. An easy way to do this would be to use the PSThreadJob module.
An experiment to demonstrate what happens to objects passed to background jobs:
$srcFi = [System.IO.FileInfo]::new( 'C:\windows\system32\ntdll.dll' )
$srcFi.GetType().FullName
start-job -ScriptBlock { param( $fi ) $fi.GetType().FullName } -Arg @( $srcFi ) | Receive-Job -Wait
Output:
System.IO.FileInfo
System.Management.Automation.PSObject
来源:https://stackoverflow.com/questions/52541648/in-powershell-pass-a-complex-object-smoserver-to-a-background-job-in-the-argu