View Single Post
Old 01-31-2008   #1 (permalink)
thoward37


 
 

Hosting Powershell in App - Obtaining delegates to powershellfunctions

Hi all, this is a follow-up to an earlier post I made regarding
embedded Powershell:

http://groups.google.com/group/micro...b8489ea0411486


In short, I'm hosting Powershell in an app as part of a rules engine,
using it to repeatedly execute user defined scripts on data provided
from a database, as one kind of "rule".

The way I initially implemented it was to create a single runspace,
then create and execute a new pipeline eachtime I needed to execute
the Powershell rule.

An example rule looks like this:

$dataValue =
$dataProviders[""SomeRandomSqlData""].GetValue(""SomeValue"");
$checkValue = $dataProviders[""StaticData""].GetValue(""MaxValue"");
if ($dataValue -eq $checkValue) { exit 5 } else { exit 0 }

The variable $dataProviders is a dictionary containing various data
providers that allow the Powershell script to make some decisions
about some data. It is passed into the Powershell world using a
statement like ...
Runspace.SessionStateProxy.SetVariable("dataProviders",
dataProviders); ... and it's updated each time I execute a pipeline
with the currently available data.

The exit code of the pipeline is checked to get the result of the
rule, which is then evaluated by the rest of the rule engine.

So, my previous post I had asked about ways to re-use a single
pipeline, instead of instantiating a new one everytime, but found that
was not possible.

Currently, this implementation works, but it's very slow, and there
are other options available for them to script in via traditional .Net
languages. Also, this code isn't even in QA at the moment, much less
released, so it's still a good time to make changes in the design...


Since then, it occured to me that a better way to do this might be as
such:

1. Instantiate Runspace
2. Pre-load the scripts into the Runspace. Each script defines a
function for each Powershell rule I'll need to run. The function takes
the dataProviders variable as a parameter, and returns an integer.
3. Obtain delegates to the loaded functions from the Runspace.
4. When the rule needs to be executed, just invoke the delegate from
my C# code.



My intention for getting the delegate to the PS function was to use
the get-delegate function described here:

http://blogs.msdn.com/powershell/arc...25/678259.aspx


So the c# delegate defintion would look like:

public delegate int PsRuleDelegate(IDictionary<string,
IRuleDataProvider> dataProviders);


The user function delegate creation code would look like this (with
each script definition getting it's own delegate variable):

.. insert get-delegate code here ..

$MyRuleDelegate = get-delegate `
My.Namespace.PsRuleDelegate {
$dataValue =
$dataProviders[""SomeRandomSqlData""].GetValue(""SomeValue"");
$checkValue =
$dataProviders[""StaticData""].GetValue(""MaxValue"");
if ($dataValue -eq $checkValue) { return 5 } else { return 0 }
}


Getting the delegate back from the runspace would look like:

Runspace.SessionStateProxy.GetVariable("MyRuleDelegate");


So, before I go fiddling with it, and trying to make that happen...
I'd like to know, is this a viable solution? Will this work? I'll
probably try it anyway tomorrow, but I'd like to hear what more
experienced and knowledgable people have to say about this idea.

I'm guessing that I will need to assemble the delgate creation
function, and all the rule function definitions into a single script,
which is loaded when the runspace first starts up, then obtain the
named delegate variables after the runspace loads. Would I then not
need to run a pipeline? Could those delegates be called directly from
my C# code without an active pipeline (but with an active runspace?).

I think that would improve the performance considerably, without
changing too much of what the end user needs to write.

Thanks,
Troy
My System SpecsSystem Spec