To get a value from your workflow you must access the dictionary of output arguments returned from the workflow.

Note: This example is based on Visual Studio 2010 Beta 2

Consider a workflow that accepts two arguments

Name Direction Argument Type
UserName In String
Greeting Out String

The workflow uses an assign activity to set the result of the expression "Hello " & UserName & " from Workflow 4" to the Greeting out argument.  The hosting program can access the Greeting out argument from the dictionary of output values returned from the WorkflowInvoker or WorkflowApplication.

Because the values in the output dictionary are stored as objects with a string as the key, there are three possible outcomes when you try to access an argument value.

  1. The key does not exist in the dictionary
  2. The key exists but the type of the value is not what you expected
  3. The key exists and the type is compatible with what you expected

Whenever you access an out argument you must consider these outcomes. In the event that the key does not exist or the type of the value is not what is expected you may encounter a KeyNotFoundException or InvalidCastException

You can choose to

  • Let these exceptions propagate possibly terminating the host with an unhandled exception
  • Catch the exceptions
  • Code defensively using TryGetValue to access the key from the collection or the C# keyword “as” or VB function TryCast to avoid the invalid cast

Accessing Out Arguments with WorkflowInvoker

The following example shows a method that Invokes the SayHello workflow passing in the UserName and accessing the out argument named “Greeting” after the workflow completes. 

Watch Out
This example is not coding defensively or catching exceptions so if the key did not exist or the type of the value was not compatible the exception would propagate to the caller.

C#

private static void GetArgumentsFromWorkflowInvoker()
{
IDictionary<string, object> output = WorkflowInvoker.Invoke(
new SayHello() { UserName = "Test" });

string greeting = (string)output["Greeting"];

Console.WriteLine("WorkflowInvoker said {0}", greeting);
}

Visual Basic

Shared Sub GetArgumentsFromWorkflowInvoker()
Dim output As IDictionary(Of String, Object) =
WorkflowInvoker.Invoke(New SayHello() With {.UserName = "Test"})

Dim greeting As String = output("Greeting")

Console.WriteLine("WorkflowInvoker said {0}", greeting)
End Sub

Accessing Out Arguments with WorkflowApplication

WorkflowApplication invokes the workflow on a thread from the CLR threadpool. To capture the outputs you must assign a delegate to the WorkflowApplication.Completed property. Keep in mind that your delegate is called whenever the workflow completes successfully or not. You can check the CompletionState property to find out if the activity closed or faulted. Because accessing the Outputs dictionary may result in an exception you should wrap the access with a try/catch/finally block as shown.

C#

private static void GetArgumentsFromWorkflowApplication()
{
AutoResetEvent sync = new AutoResetEvent(false);
string greeting = null;
Exception argException = null;

WorkflowApplication wfApp = new WorkflowApplication(
new SayHello()
{
UserName = "Test"
});

wfApp.Completed = (e) =>
{
// Did the workflow complete without error?
if (e.CompletionState == ActivityInstanceState.Closed)
{
try
{
// Accessing the output arguments dictionary
// might throw a KeyNotFoundException or
// InvalidCastException
greeting = (string)e.Outputs["Greeting"];
}
catch (Exception ex)
{
argException = ex;
}
finally
{
// Must be sure to unblock the main thread
sync.Set();
}
}
};

wfApp.Run();
sync.WaitOne();

// Show the exception from the background thread
if (argException != null)
Console.WriteLine("WorkflowApplication error {0}", argException.Message);
else
Console.WriteLine("WorkflowApplication said {0}", greeting);
}

Visual Basic

Shared Sub GetArgumentsFromWorkflowApplication()
Dim sync As AutoResetEvent = New AutoResetEvent(False)
Dim greeting As String = Nothing
Dim argException As Exception = Nothing
Dim wfApp As WorkflowApplication =
New WorkflowApplication(New SayHello() With {.UserName = "Test"})

wfApp.Completed = Function(args)
If (args.CompletionState =
ActivityInstanceState.Closed) Then
Try
' Accessing the output arguments dictionary
' might throw a KeyNotFoundException or
' InvalidCastException
greeting = args.Outputs("Greeting")
Catch ex As Exception
argException = ex
Finally
' Must be sure to unblock the main thread
sync.Set()
End Try
End If

' VB requires lambda expressions to return a value
Return Nothing
End Function

wfApp.Run()
sync.WaitOne()

' Show the exception from the background thread
If (argException Is Nothing) Then
Console.WriteLine("WorkflowApplication said {0}", greeting)
Else
Console.WriteLine("WorkflowApplication error {0}", argException.Message)
End If
End Sub


Accessing Out Arguments with Defensive Coding Style

In this example, we are accessing the Out Argument with a defensive coding style that will insure no exceptions are thrown.

ContainsKey() vs. TryGet()
You should use TryGet instead of ContainsKey() to first check for the key and then Get() to access the key. The reason for this is that you will iterate over the collection twice, once to determine if the key is present and again to access the value.

C#

private static void GetArgumentsFromWorkflowInvokerDefensive()
{
IDictionary<string, object> output = WorkflowInvoker.Invoke(
new SayHello() { UserName = "Test" });

object obj = null;
string greeting = null;

if (!output.TryGetValue("Greeting", out obj))
{
Console.WriteLine("Greeting not found");
}
else
{
greeting = obj as string;
if (greeting == null)
Console.WriteLine("Greeting could not be converted to a string");
else
Console.WriteLine("WorkflowInvoker said {0}", greeting);
}
}

Visual Basic

Shared Sub GetArgumentsFromWorkflowInvokerDefensive()
Dim output As IDictionary(Of String, Object) =
WorkflowInvoker.Invoke(New SayHello() With {.UserName = "Test"})

Dim obj As Object = Nothing

Dim greeting As String

' TryGetValue will not throw an exception
If (Not output.TryGetValue("Greeting", obj)) Then
Console.WriteLine("Greeting not found")
Else
' Not sure what type it is, try to convert it
greeting = TryCast(obj, String)
If (greeting Is Nothing) Then
Console.WriteLine("Greeting could not be converted to a string")
Else
Console.WriteLine("WorkflowInvoker said {0}", greeting)
End If
End If
End Sub