Windows Workflow 4: Adding Items to a Collection

I wanted to create a workflow that executes several custom activities in parallel and adds the results from each to a shared collection. This is easily accomplished by using WF4’s AddToCollection activity.

The XAML below demonstrates the use of both the Parallel and AddToCollection activities to accomplish this task. I’m also using of the ForEach activity to iterate through my collection to display the results.

<Activity xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" 
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib">
  <Sequence>
    <Sequence.Variables>
      <Variable x:TypeArguments="scg:List(x:String)" Default="[New List(Of String)]" Name="MyList" />
    </Sequence.Variables>

    <Parallel>
        <AddToCollection x:TypeArguments="x:String" Collection="[MyList]" Item="item1" />
        <AddToCollection x:TypeArguments="x:String" Collection="[MyList]" Item="item2" />
        <AddToCollection x:TypeArguments="x:String" Collection="[MyList]" Item="item3" />
    </Parallel>
    
    <ForEach x:TypeArguments="x:String" Values="[MyList]">
        <ActivityAction x:TypeArguments="x:String">
            <ActivityAction.Argument>
                <DelegateInArgument x:TypeArguments="x:String" Name="item" />
            </ActivityAction.Argument>
            <WriteLine Text="[item]" />
        </ActivityAction>
    </ForEach>
  </Sequence>
</Activity>

Here’s the output produced by running this workflow:

Some notes about this example:

  • The XAML was written without the use of Visual Studio’s designer
  • There is a collection variable of type List<string> declared named “MyList”; this requires us to import the namespace System.Collections.Generic
  • The AddToCollection activities’ Collection properties are bound to MyList
  • The ForEach activity’s Values property is bound bound to MyList
  • The ForEach activity creates a delegate argument named “item” which is then passed in to the ForEach activity’s WriteLine activity
Advertisement

Windows Workflow 4: Creating a Simple Custom Activity

The Windows Workflow Foundation project that I’m working on relies on my ability to create a library of custom activities that can be used together to handle many different customer scenarios. Creating a simple custom activity is a very easy and straightforward task with WF4. This post will walk you through the steps of creating a custom send-email activity.

The first thing you’ll need to do is create a new class that inherits from NativeActivity. (Note that NativeActivity is not your only option–check out this post for an overview of the different choices.) NativeActivity is an abstract class that requires us to implement an Execute method. This is the method that runs when the activity executes.

public sealed class EmailActivity : NativeActivity
{
    protected override void Execute(NativeActivityContext context)
    {
        throw new NotImplementedException();
    }
}

We’ll need some additional information in order to send an email, and we can use input arguments to get what we need. To add an input argument, create a public property of type InArgument<T>. Here are my input properties. Notice that I’ve decorated some of them with the [RequiredArgument] attribute to indicate that they are required. Output arguments can be added in a similar fashion by using OutArgument<T>, though we do not need any for this example.

[RequiredArgument]
public InArgument<string> Host { get; set; }

[RequiredArgument]
public InArgument<string> Sender { get; set; }

[RequiredArgument]
public InArgument<string> Recipients { get; set; }

public InArgument<string> Subject { get; set; }

public InArgument<string> Body { get; set; }

Now that we have all the information we need, we just need to implement the Execute method to send an email. Once again, it’s very simple.

protected override void Execute(NativeActivityContext context)
{
    // create the email message
    var mailMessage = new MailMessage();
    mailMessage.From = new MailAddress(Sender.Get(context));
    mailMessage.To.Add(new MailAddress(Recipients.Get(context)));
    mailMessage.Subject = Subject.Get(context);
    mailMessage.Body = Body.Get(context);

    // send the message
    var smtpClient = new SmtpClient(Host.Get(context));
    smtpClient.Send(mailMessage);
}

This activity can now be added to your workflow XAML through the designer or any text editor. In the example below, I have bound the Body property to a variable named StatusText that will be set in an earlier activity.

<my:EmailActivity
  Host="mail.mydomain.com"
  Sender="myworkflow@mydomain.com"
  Recipients="someuser@mydomain.com"
  Subject="Workflow Status Update"
  Body="[StatusText]" />

Here is the complete class:

public sealed class EmailActivity : NativeActivity
{
    [RequiredArgument]
    public InArgument<string> Host { get; set; }

    [RequiredArgument]
    public InArgument<string> Sender { get; set; }

    [RequiredArgument]
    public InArgument<string> Recipients { get; set; }

    public InArgument<string> Subject { get; set; }

    public InArgument<string> Body { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        // create the email message
        var mailMessage = new MailMessage();
        mailMessage.From = new MailAddress(Sender.Get(context));
        mailMessage.To.Add(new MailAddress(Recipients.Get(context)));
        mailMessage.Subject = Subject.Get(context);
        mailMessage.Body = Body.Get(context);

        // send the message
        var smtpClient = new SmtpClient(Host.Get(context));
        smtpClient.Send(mailMessage);
    }
}

Windows Workflow 4: Dynamically Load Workflows

I’m starting a new project where I plan to use Windows Workflow Foundation to load custom jobs from external XML files like plug-ins. In order for this to work, I’ll have a sub-directory that my application watches. If files are found, they will be loaded and executed.

The reason I want to do this is because Windows Workflow Foundation 4 makes it easy to keep the workflows as human-readable XML files that can be edited by technical-but-not-developer users. This allows for easy, efficient customization of workflows. The processes we have now require developer interaction for any customization or modification since everything is compiled into DLLs making our development team an unnecessary bottleneck for warranty and enhancement.

Over the next days and weeks, I’ll be publishing a number of posts that demonstrate small nuggets of WF4 functionality. I’ve been impressed with what I’ve been able to accomplish in just a few days of fooling around, so this I’m very excited about this endeavor!

I’m keeping it short and simple with this post: loading and running an XML (XAML) workflow from a file at runtime. This very powerful functionality can be accomplished in a single line of code:

WorkflowInvoker.Invoke(ActivityXamlServices.Load("HelloWorld.xml"));

One of my main goals is encapsulating all of my functionality in human-readable XML, so I also wanted to provide the contents of my HelloWorld.xml file. This workflow is unimpressive and simply writes “Hello, world!” to the console, but here it is, nonetheless:

<Activity xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" 
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <WriteLine Text="Hello, world!" />
</Activity>
%d bloggers like this: