Powershell New-WebServiceProxy 异步方法

Powershell New-WebServiceProxy Async methods

我有一个 WCF 库公开了一些有用的信息,我使用 New-WebServiceProxy cmdlet

从 powershell 查询它们
$link = "http://localhost/Rlib/Rlib.Info.svc";
$service = New-WebServiceProxy -Uri $link;

假设我有一个像GetLastData()这样的函数,我通常这样调用它

$service.GetLastData()

现在我有一个非常慢的函数,需要很多时间才能完成(return 一个日期的复杂数据库查询),我看到一个异步函数是自动生成的,我可以使用 $service.GetLastDataAsync()。它是如何工作的?调用异步方法后,如何检查数据是否可用并恢复它们?

How does it work?

GetLastDataAsync returns Task<something>,可以用来跟踪一个出库操作的过程。它会变得相当复杂。您可能不需要使用异步版本,除非您有充分的理由吗?您可以将 powershell 作业用于同步版本,并只轮询作业的运行情况。

Once I call the async method how can I check if data are available and recover them?

如果你想等待结果,你可以做 GetLastDataAsync(..).Result,但这没有用,因为你已经可以使用 同步

我认为没有简单的方法可以正确使用 Task。我发现最接近的是以这种方式使用某种自定义 DLL:

    C:\PS> $path = "$home\Pictures\foo.png"
    C:\PS> $asyncOp = [Windows.Storage.StorageFile]::GetFileFromPathAsync($path)
    C:\PS> Add-Type -Path ~\PoshWinRT.dll
    C:\PS> $typeName = 'PoshWinRT.AsyncOperationWrapper[Windows.Storage.StorageFile]'
    C:\PS> $wrapper = new-object $typeName -Arg $asyncOp
    C:\PS> $file = $wrapper.AwaitResult()
    C:\PS> $file

wrapper 背后的理论是它在幕后使用 Task.Wait,这在某种程度上超过了 async 的全部目的。因为它仍然阻塞主线程。

TLDR;如果需要,坚持使用同步版本和预定作业。

https://rkeithhill.wordpress.com/2013/09/30/calling-winrt-async-methods-from-windows-powershell/

调用Async方法后,操作完成时会引发相应的事件(在您的情况下GetLastDataCompleted)。

您可以使用 Register-ObjectEvent 到 "listen" 来表示何时引发此事件!

我使用 CDYNE's IP2Geo service 进行了快速测试,但这个想法与任何其他 SOAP 服务完全相同:

$ServiceUri = 'http://ws.cdyne.com/ip2geo/ip2geo.asmx?wsdl'
$Service = New-WebServiceProxy -Uri $ServiceUri

# Make sure the environment is clean
Remove-Variable -Scope Global -Name IPResult -Force | Out-Null
Unregister-Event -SourceIdentifier "IPResolved"

# Register an event subscription
Register-ObjectEvent -InputObject $Service -EventName ResolveIPCompleted -SourceIdentifier "IPResolved" -Action { $Global:IPResult = $args[1] } | Out-Null

# Initialize the operation with Async
$Service.ResolveIPAsync('8.8.8.8','')

# Do other stuff in the background here

# Wait for the variable to get populated
while(-not(Get-Variable -Scope Global -Name IPResult -ErrorAction SilentlyContinue)){
    Start-Sleep -Seconds 1
}

# Let's see what we got!
if($Global:IPResult.Error -eq $null){
    Write-Host "Success!"

    # The Result property holds the result from the operation
    $Global:IPResult.Result

} else {
    Write-Host "Failed!"
}