Executing Powershell from C#

This topic contains 4 replies, has 3 voices, and was last updated by Profile photo of Ritmo2k Ritmo2k 2 years, 4 months ago.

  • Author
    Posts
  • #17280
    Profile photo of ritmo2k
    ritmo2k
    Participant

    I know the C# element of this is OT here but I am at a loss and am hopeful someone with more powershell experience than I may know something.

    I have tried several ways to loop through some code with powershell invoked from with C# but each invocation runs painfully slow and degrades very quickly to almost a halt.
    If I create and dispose of the System.Management.Automation PowerShell instance for each iteration the speed maintains its pace for slightly longer but degrades as well.
    Ultimately the speed of a single invocation is dreadful.

    My use case is calling a function and sending in a some strings and dictionaries of lists as parameters. I need to do this a few million times.
    The C# app queries a database for input and writes back processed data. If not invoking powershell that process alone is extremely fast.

    A thought would be to never leave the ps execution environment and expose some C# classes to the ps environment that yield input and consume output.
    The loop would run within a single ps invocation. If possible, that class that provides data utilizes a generator, so I certainly would not want to consume its dataset before iterating as that would consume far too much memory. I could create some code that accepts this properly via a pipeline.

    What are any thoughts from people who have been down this road?

    Thanks!

  • #17281
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    We probably need to see what you've already attempted, but in general, PowerShell is very slow compared to C#. If performance is a concern, since you're in a C# app anyway, you may find that it's better to leave PowerShell out of the picture entirely.

  • #17284
    Profile photo of Ritmo2k
    Ritmo2k
    Participant

    Hi Dave,
    I have basically moved the following into and out of the loop that iterates of the data set:

    Collection<PSObject> results;
    using (PowerShell powerShell = PowerShell.Create())
    {
        // Source functions.
        powerShell.AddScript(PsScript);
        powerShell.Invoke();
    
        // Call function contained in sourced script above.
        powerShell.AddCommand("My-Function");
        powerShell.AddParameter("ParamA", varA);
        powerShell.AddParameter("ParamB", varB);
        powerShell.AddParameter("ParamC", varC);
        powerShell.AddParameter("ParamD", varD);
    
        results = powerShell.Invoke();
    }
    

    I'd love to remove the powershell but this is the premise of the program, it provides an easy to modify scriptable environment.
    Its fundamental that for each usage of the compiled code, you have access to user defined functions.

    Aside from the above, I have tried all sorts of appraoches using RunSpaces etc to no avail.

    Thanks!

  • #17291
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Well, that's the basics of how to execute PowerShell from within C#. However, if you're doing this millions of times, you may find that it's much faster to run the script once, keep an open runspace, and then run the function multiple times. That way you're not forcing PowerShell to parse and compile the script every time through the loop. Something along these lines:

    using (var runspace = RunspaceFactory.CreateRunspace())
    {
        using (var powerShell = PowerShell.Create())
        {
            powerShell.Runspace = runspace;
            powerShell.AddScript(PsScript);
            powerShell.Invoke();
        }
    
        foreach (var thingy in thingies)
        {
            using (var powerShell = PowerShell.Create())
            {
                powerShell.Runspace = runspace;
    
                powerShell.AddCommand("My-Function");
                powerShell.AddParameter("ParamA", varA);
                powerShell.AddParameter("ParamB", varB);
                powerShell.AddParameter("ParamC", varC);
                powerShell.AddParameter("ParamD", varD);
     
                var results = powerShell.Invoke();
    
                // Do whatever with results
            }
        }
    }
    

    (Note: I wrote this in Notepad, and haven't tested it. It probably doesn't work as-is.)

  • #17300
    Profile photo of Ritmo2k
    Ritmo2k
    Participant

    Dave,
    That actually made such a significant difference that the original approach may just be feasible.

    Very much appreciated!

You must be logged in to reply to this topic.