Ron Jacobs

Windows Workflow Foundation

Passing arguments to Workflow Activities (again)

Passing arguments to Workflow Activities (again)

  • Comments 1

Way back in September 2009 I wrote WF4: Passing Arguments to Activities.  In the years since I’ve learned a few things.

Download code for this post Windows Workflow Foundation (WF4) - Workflow Arguments Example

Watch Out for Initialization Syntax

I used to like the way we made a Workflow definition look like any other CLR object even allowing you to do initialization syntax for some types.

static void Main(string[] args)
{
    // What is SayHello?  It looks like any other CLR object
    WorkflowInvoker.Invoke(new SayHello() { Name = "Ron" });
}

API Hypocrisy

I once heard someone say that Hypocrisy is behavior that tells a lie.  It looks like one thing is happening, but in reality something very different is going on.  What you see in the code above is an example of this.  It looks like a simple reference but what is actually going on is we are constructing an InArgument<T> by taking the string value “Ron” and converting it into a Literal<T>.

What happens if the argument is not something that can be treated as a literal.  Suppose we create a Person object?

static void Main(string[] args)
{
    var me = new Person() { Name = "Ron", Age = 46 };
 
    // Will this work?
    WorkflowInvoker.Invoke(new SayHello() { Person = me });
}

Not so fast…  When you run this you get an exception

System.Activities.InvalidWorkflowException was unhandled
  Message=The following errors were encountered while processing the workflow tree:
'Literal<Person>': Literal only supports value types and the immutable type System.String.  The type WorkflowConsoleApplication1.Person cannot be used as a literal.

Now what?  The hypocrisy is revealed!  The activity you are working with is not just any old CLR object.  Not to mention that if you create a new one every time you will feel significant performance pain.

If you want to use a reference type you have to create a Dictionary<string, object> to pass the arguments like this

private static void SayHello3()
{
    var me = new Person { Name = "Ron", Age = 46 };
 
    // have to construct a dictionary
    var input = new Dictionary<string, object> { { "Person", me } };
 
    // And pass it to the activity
    var output = WorkflowInvoker.Invoke(new SayHello(), input);
 
    // have to access the output with indexer
    Console.WriteLine("Workflow said {0}", output["Greeting"]);
}
 

Input Dictionary vs. Property Syntax

What happens if you use both a property initializer and the input dictionary? 

private static void SayHelloSimpleWithBoth()
{
    Console.WriteLine("What if you use both property initializer and input dictionary?");
    var activityDefinition = new SayHelloSimple() { Name = "Initializer" };
    var input = new Dictionary<string, object> { { "Name", "Input Dictionary" } };
    WorkflowInvoker.Invoke(activityDefinition, input);
    // Hint: The Input Dictionary wins every time...
}

What this means is that the initializer syntax creates a default value that will be used if no value is supplied by the input dictionary.

New Microsoft.Activities.WorkflowArguments Class

After exploring ASP.NET MVC for a while and working with the ViewBag dynamic class I thought wouldn’t it be cool if I could do the same thing for Workflow arguments.  So I added a new class to Microsoft.Activities v1.83.

To use it, I just install the package with NuGet Package Manager which installs the package and adds a reference for me.

PM> install-package Microsoft.Activities
Successfully installed 'Microsoft.Activities 1.8.3.526'.
Successfully added 'Microsoft.Activities 1.8.3.526' to WorkflowConsoleApplication1.

Then I can modify my code like this

private static void SayHello4()
{
    // Create a dynamic object
    dynamic input = new WorkflowArguments();
 
    // The property names have to match the workflow argument names
    input.Person = new Person { Name = "Ron", Age = 46 };
 
    // pass it to the activity no need to cast it
    // You can do the same on the output
    var output =  WorkflowArguments.FromDictionary(WorkflowInvoker.Invoke(new SayHello(), input));
 
    // Access the output with property syntax
    Console.WriteLine("Workflow said {0}", output.Greeting);
 
    // Or access the output with indexer
    Console.WriteLine("Workflow said {0}", output["Greeting"]);
}

And it totally works!  Are dynamic objects API Hypocrisy?  Maybe.  It’s just a little syntactic sugar over the inner dictionary but it is a lot of fun.

Happy Coding!

Ron Jacobs
http://blogs.msdn.com/rjacobs
Twitter: @ronljacobs http://twitter.com/ronljacobs

  • Hy ron

    Thanks for sharing. I added a similar helper some time ago to the ninject.extensions.wf package which I created last year.

    github.com/ ninject/ninject.extensions.wf

    Happy coding!

Page 1 of 1 (1 items)