背景资讯:
我有一个应用程序可以与多个数据库建立多个SQL连接,这些数据库目前需要很长时间才能执行。
Powershell(.NET)将等待每个进程“ SQL-GET ”函数完成,然后才能触发下一个。我的印象是我可以通过在他们自己的后台作业中同时触发每个“ SQL-GET ”函数来显着提高这个应用程序!然后我将在每个作业完成时检索数据。理想情况下,作为DataSet系统对象。
问题:
从后台作业中检索数据时,我只能设法获取 System.Array 对象。我实际上是一个 System.DataSet 对象。这是必要的,因为应用程序中的所有逻辑都依赖于DataSet对象。
守则:
这是一个v.simple代码片段,它将创建一个sql连接并用返回的结果填充一个新创建的数据集对象。工作一种享受。 $ results 是一个DataSet对象,我可以很好地操作它。
$query = "SELECT * FROM [database]..[table] WHERE column = '123456'"
$Connection = New-Object System.Data.SqlClient.SQLConnection
$ConnectionString = "Server='SERVER';Database='DATABASE';User ID='SQL_USER';Password='SQL_PASSWORD'"
$Connection.ConnectionString = $ConnectionString
$Connection.Open()
$Command = New-Object system.Data.SqlClient.SqlCommand($Query,$Connection)
$Adapter = New-Object system.Data.SqlClient.SqlDataAdapter
$Adapter.SelectCommand = $Command
$Connection.Close()
[System.Data.SqlClient.SqlConnection]::ClearAllPools()
$results = New-Object system.Data.DataSet
[void]$Adapter.fill($results)
$results.Tables[0]
这就是将非常相同的代码包装到新后台作业的scriptblock参数中。只有在调用Receive-Job时,才会返回数组,而不是数据集。
$test_job = Start-Job -ScriptBlock {
$query = "SELECT * FROM [database]..[table] WHERE column = '123456'"
$Connection = New-Object System.Data.SqlClient.SQLConnection
$ConnectionString = "Server='SERVER';Database='DATABASE';User ID='SQL_USER';Password='SQL_PASSWORD'"
$Connection.ConnectionString = $ConnectionString
$Connection.Open()
$Command = New-Object system.Data.SqlClient.SqlCommand($Query,$Connection)
$Adapter = New-Object system.Data.SqlClient.SqlDataAdapter
$Adapter.SelectCommand = $Command
$Connection.Close()
[System.Data.SqlClient.SqlConnection]::ClearAllPools()
$results = New-Object system.Data.DataSet
[void]$Adapter.fill($results)
return $results.Tables[0]
}
Wait-Job $test_job
$ret_results = Receive-Job $test_job
任何帮助都将非常感谢!!!
远远研究:
我已经完成了旧的谷歌,但我偶然发现的所有帖子,博客和文章似乎都进入了极端的关于管理工作以及围绕这个问题的所有花里胡哨的深度。 PowerShell的基本特性是只通过receive-job cmdlet返回一个数组吗?
我已阅读有关返回表达式的stack post。以为我接受了一些事情。试图:
return $results.Tables[0]
return ,$results.Tables[0]
return ,$results
所有仍然返回一个数组。
我已经看到人们,相当繁琐,手动将数组转换回数据集对象 - 虽然这看起来非常'肮脏' - 我很迂腐,希望必须有一种方法让这个神奇的数据集对象遍历后台工作和我当前的会议! :)
重申:
基本上,我想要的是将从Rece-Job cmdlet中检索到的$ ret_results对象作为DataSet ...甚至是DataTable。我会采取......不仅仅是阵列:)
答案 0 :(得分:2)
在powershell中,一组多个任意类型的对象在集合中返回是很常见的。考虑这个改变的例子,我建立自己的表:
PS C:\> $job = Start-Job -ScriptBlock {
>>
>> $table = New-Object system.Data.DataTable “MyTable”
>>
>> $col1 = New-Object system.Data.DataColumn MyFirstCol,([string])
>> $col2 = New-Object system.Data.DataColumn MyIntCol,([int])
>>
>> $table.columns.add($col1)
>> $table.columns.add($col2)
>>
>> $row1 = $table.NewRow()
>> $row1.MyFirstCol = "FirstRow"
>> $row1.MyIntCol = 1
>> $row2 = $table.NewRow()
>> $row2.MyFirstCol = "SecondRow"
>> $row2.MyIntCol = 2
>>
>> $table.Rows.Add($row1)
>> $table.Rows.Add($row2)
>>
>> $dataSet = New-Object system.Data.DataSet
>> $dataSet.Tables.Add($table)
>>
>> $dataSet.Tables[0]
>>
>> }
>>
PS C:\> $output = Receive-Job -Job $job
收到的输出。那我们得到了什么?
PS C:\> $output.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
正如您所描述的那样一个数组。但这就是整个对象。如果我们通过将它们分配到Get-Member
来单独分析其成员,该怎么办?
PS C:\> $output | gm
TypeName: Deserialized.System.Data.DataRow
Name MemberType Definition
---- ---------- ----------
ToString Method string ToString(), string ToString(string format, System.IFormatProvider formatProvi...
PSComputerName NoteProperty System.String PSComputerName=localhost
PSShowComputerName NoteProperty System.Boolean PSShowComputerName=False
RunspaceId NoteProperty System.Guid RunspaceId=186c51c3-d3a5-404c-9a4a-8ff3d3a7f024
MyFirstCol Property System.String {get;set;}
MyIntCol Property System.Int32 {get;set;}
PS C:\> $output
RunspaceId : 186c51c3-d3a5-404c-9a4a-8ff3d3a7f024
MyFirstCol : FirstRow
MyIntCol : 1
RunspaceId : 186c51c3-d3a5-404c-9a4a-8ff3d3a7f024
MyFirstCol : SecondRow
MyIntCol : 2
请考虑以下事项:
在您的工作中,您已指定应返回$results.Tables[0]
。通过指定特定的表迭代,您将返回描述该表的对象...可能是DataTable,或者在这种情况下是DataRows ...而不是像您似乎期待的DataSet?
DataTables有行。如果DataTable有多行,那么powershell会在DataRows集合中返回它,正如我在上面演示的那样。您可能会惊讶地发现,返回单行不是这种情况 - 它只返回单个DataRow对象而不是DataRow对象的集合。
如果这确实是您期望的输出,您可能希望通过将输出指定为@($results.Tables[0])
来强制它始终在集合中返回。这样,您总是知道期望一个集合并且可以适当地处理结果内容(通过遍历集合来管理单个对象)。
答案 1 :(得分:0)
当您将脚本作为PS作业运行时,您正在创建一个新进程(pid),并且实际上无法从父作业中获得相同的对象。通过Receive-Job cmdlet收到的是该对象的反序列化副本(所有属性都将转换为基本类型(如字符串/数字/等),并且方法将被删除。
但是有一个解决方案-运行空间。运行空间是在同一pid中作为独立线程创建的子进程。它几乎是异步功能(脚本块)的执行。检查以下示例:
$script = {
$dt = new-object System.Data.DataTable
$dt.Columns.add() | Out-Null
$dt.Columns.add() | Out-Null
$dt.Rows.Add(1,2) | Out-Null
$dt.Rows.Add(3,4) | Out-Null
$ds = New-Object System.Data.DataSet
$ds.Tables.Add($dt)
Write-Output @{ds = $ds}
}
$PowerShell = [powershell]::Create()
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell.runspace = $Runspace
$Runspace.Open()
[void]$PowerShell.AddScript($script)
$result = $PowerShell.Invoke()
$result.ds.gettype()
此代码在运行空间中执行$ script脚本块。此示例未异步运行(需要使用BeginInvoke / EndInvoke,为简单起见,跳过了该示例),但是如您所见,它返回的是实际的DataSet / DataTable,而不是PSObject
要了解更多信息,请查看脚本专家的这些文章: https://blogs.technet.microsoft.com/heyscriptingguy/2015/11/26/beginning-use-of-powershell-runspaces-part-1/ 他还创建了一个PoshRSJob模块-它镜像标准作业cmdlet,但改用Runspaces(带有异步执行)