Может ли Powershell Receive-Job вернуть набор данных?

Справочная информация:

У меня есть приложение, которое устанавливает несколько 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

Любая помощь будет принята с благодарностью!!!

Исследование пока что:

Я работал со старым Google, но все посты, блоги и статьи, с которыми я сталкиваюсь, кажется, проникают в ЧРЕЗВЫЧАЙНУЮ глубину об управлении рабочими местами и всеми прибамбасами вокруг этого. Является ли базовая природа powershell, чтобы ТОЛЬКО возвращать массив через командлет receive-job?

Я прочитал сообщение стека о выражении возврата. Думал, что я был к чему-то. Покушение:

  • return $results.Tables[0]
  • return ,$results.Tables[0]
  • return ,$results

Все еще возвращают массив.

Я видел, как довольно громоздко люди вручную преобразовывали массив обратно в объект набора данных - хотя это кажется очень "грязным" - я педантичен и живу надеждой, что у этого магического объекта набора данных должен быть способ пройти через фоновую работу и в мою текущую сессию!:)

Повторить:

По сути, все, что я хотел бы, - это чтобы объект $ret_results был извлечен из командлета Receive-Job в качестве DataSet... или даже DataTable. Я возьму либо... просто не массив:)

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]), Таким образом, вы всегда знаете, что ожидаете коллекцию, и можете соответствующим образом обработать полученное содержимое (перебирая коллекцию для управления отдельными объектами).

Когда вы запускаете Start-Job, он в значительной степени генерирует новую оболочку / область видимости. Я предполагаю, что единственный способ обмена данными между различными оболочками - это использование некоторого текстового формата (например, XML/JSON), поэтому выходные данные дочернего задания будут преобразованы в этот "XML", а родительское задание преобразует его обратно в PSObject или Array. Поэтому вам нужно проделать некоторую работу, чтобы получить исходный объект.Net в родительской работе. Я работал над аналогичным проектом, вытащил таблицы данных из 5 баз данных и затем скопировал их в одну таблицу отчетности. В качестве окончательного решения я решил выполнить массовую копию в дочерних заданиях (потому что SQL-сервер поддерживает параллельную BC для неиндексированных таблиц). Если бы мне нужно было сделать это из родительской работы, я бы преобразовал PSObject в datatable с помощью командлета Out-DataTable

Другие вопросы по тегам