简体   繁体   中英

PowerShell System.Management.Automation.Runspaces vs TAP in C#

I've recently run into a use case where the "Async" suffix recommended by the .NET Task-based Asynchronous Pattern (TAP) conflicts with what's already in existence.

I'm dealing with System.Management.Automation.Runspaces.Runspace during the course of attempting PowerShell remoting to execute cmdlets as part of my app.

Ignoring the questions that arise around whether it's best practice to knock-up a remoting session each time you want to run a cmdlet (for an enterprise scale application this might be a lot) or to create a connection and attempt to maintain it during the app's lifetime (with reconnection logic)...

My application is based on TAP which proliferates from the WebApi2 controller all the way down to the backend, what I'm trying to do is asynchronously open a Runspace connection - but noticed that there's already an OpenAsync method which isn't awaitable and returns void - which is like some weird mash-up between async void (for event handlers), void (non-async) and the Async suffix.

I'm using Stephen Cleary's Nito.AsyncEx nuget package to provide me with a AsyncAutoResetEvent which I can asynchronously await before attempting connection/reconnection).

The question is: should I care about the fact that my code really isn't going to be properly "async" in using either Open or OpenAsync on the Runspace ?

If I should care - what's the best practice in this situation? It doesn't look like Microsoft have released updated DLLs which provide awaitable Open methods for the Runspace . Strangely despite MS giving information on how to use these libraries, they've added the caveat on the nuget site :

Versions 6.1.7601.* are unofficial packages for .Net 4.0 and are not supported by Microsoft.

There also seems to be this DLL-esque package on nuget from Microsoft, aagggghh!

Currently my plan is something akin to this:

public async Task<Result> StartAsync()
{
    if (!IsConnected)
    {
        try
        {
            await _asyncRunspaceLock.WaitAsync();
            if (!IsConnected)
            {
                var protocol = IsHttpsEnabled ? "https" : "http";
                var serverUrl = $"{protocol}://{Fqdn}/OcsPowershell";
                var uri = new Uri(serverUrl);
                var connectionInfo = new WSManConnectionInfo(uri, ShelUri, PSCredential.Empty)
                {
                    SkipRevocationCheck = true,
                };
                var runspace = runspaceFactory.CreateRunspace(connectionInfo);                
                runspace.OpenAsync();
            }
        }
        catch (Exception ex)
        {
            // TODO: Handle logging the 3rd party exception at the lowest level.
            return Result.Fail(ex.Message);
        }
        finally
        {
            _asyncRunspaceLock.Set();
        }
    }

    return Result.Ok();            
}

It's a work in progress, I guess the same issue crops up around the RunspaceFactory's CreateRunspace static method which isn't async (at least it isn't named with the Async suffix).

Any helpful advice or experience would be greatly appreciated.

Thanks

peteski

From the documentation :

If you're adding a TAP method to a class that already contains that method name with the Async suffix, use the suffix TaskAsync instead. For example, if the class already has a GetAsync method, use the name GetTaskAsync .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM