Practical .NET

Simplify Workflow with Custom Activities

The number of built-in Activities that you can use to create a service that handles a long running service is small. Fortunately, it's easy to add additional Activities that wrap up business logic.

In my article on using Workflow to support long running operations in an service-oriented kind of way, I mentioned in passing that you have the option to create custom activities for your workflows. Here's how to do that.

In a workflow, you can call any method on any class by using the InvokeMethod Activity. It's a general-purpose tool;  however, that doesn't provide much support for the specific activity you're performing. That's especially obvious when you start passing parameters to the Activity. You don't get any IntelliSense support, just some error messages that will, at best, tell you if you have the right number of parameters of the right type. Creating your own custom Activities gives you an Activity dedicated to whatever task you want to perform with named, data-typed properties you can set reliably.

A custom Activity can be a simple wrapper around some existing method you'd otherwise call using the InvokeMethod activity, or your Activity can hold the actual code you want to execute.

To create a custom Activity, first add a class to your project and have it inherit from System.Activities.CodeActivity (under Add New Item, select the Activity template in the Workflow section):

 Imports System.Activities

Public NotInheritable Class SendEmails
Inherits CodeActivity

If you're only going to be using your custom Activity in one workflow, add it to the Workflow project. If you think your activity will be useful in multiple Workflows, create the class in a separate Class Library project.

Your Activity must override the Execute method of CodeActivity class (this is the method called by Windows Workflow when your Activity is processed). Workflow will pass that method a CodeActivityContext object, which gives you access to data in (and about) the Workflow:

Protected Overrides Sub  Execute(ByVal context As  CodeActivityContext)

End Sub

You next step is to define the properties a developer will use to pass values to your Activity. These properties must be declared using the generic InArgument<T>, specifying the data type you want the parameter to have. For any required parameter, you can decorate the property with the System.Activites.RequiredFieldAttribute to ensure that a developer using your Activity realizes that a value must be provided.

These two lines define two input values, one as a string and one as a Customer object (the CustomerParameter is required):

Property Body() As InArgument(Of String)
Property CustomerToEmail() As InArgument(Of Customer)

Within your Execute method, you can retrieve the value of the parameter with the GetValue method of the CodeActivityContext object passed to the method, passing a reference to the Property you want to retrieve:
cust = context.GetValue(Me.Cust)

There's a corresponding SaveValue method you could use to return a value through one of these properties. To use that, declare the property as either an OutArgument or an InOutArgument. However, if you do need to return a value, it's a better practice to inherit from the CodeActivity<T> class, specifying the data type you'll return. The CodeActivity<T> class still includes an Execute method, but now it's a function that returns a value of the type you specified when you declared the class.

This example, for instance, creates an Activity that returns a Boolean value:

Public NotInheritable Class SendEmail
Inherits CodeActivity(Of Boolean)

Protected Overrides Function Execute(
context As System.Activities.CodeActivityContext) As Boolean
Return True

End Function

After you've finished coding your class, rebuild your application and you'll find that your Toolbox includes a new icon for the Activity you've just defined—you just have to drag it into your workflow and set its properties to have it execute in your workflow.

In addition to accessing the properties set on your Activity, it's also possible to access “Workflow-level” variables from within your Execute method. Like using an OutArgument, it's not a best practice (you're tying your CustomActivity to what are, effectively, global variables) but, in case you need it, here's the code to retrieve and set a Workflow variable:

Dim props As System.ComponentModel.PropertyDescriptorCollection
props = context.DataContext.GetProperties()
cust = CType(props("CurrentCustomer").
GetValue(context.DataContext), CustomerManagement.Customer)
cust.LastName = "Irvine"
props("CurrentCustomer").SetValue(context.DataContext, cust)

This is just the simplest custom Activity you can create. If, for instance, you have a long-running method, rather than pause the Workflow while you wait for the method to complete, inherit from ASyncCodeActivity (it has both a BeginExecute and EndExecute method); if you need something more sophisticated than a class with a single method, inherit from the NativeActivity class; if you want to coordinate the actions of other Activities, you can extend the Activity class itself. Really, whatever your need is there's an Activity object waiting for you to extend.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at

comments powered by Disqus


  • VS Code Python Tool Does Multiple Interactive Windows

    Code cells from Python scripts by default will still be executed in a same interactive window, but developers can now configure the Python extension to run separate files in separate interactive windows.

  • VS Code Java Team Improves 'Getting Started' Experience

    Microsoft's dev team responsible for the Java on Visual Studio Code extensions released a new update that eases the "getting started" experience, addressing feedback from new users who want an easier onramp.

  • Data Prep for Machine Learning: Encoding

    Dr. James McCaffrey of Microsoft Research uses a full code program and screenshots to explain how to programmatically encode categorical data for use with a machine learning prediction model such as a neural network classification or regression system.

  • Surface Duo Debut Presents Dual-Screen Dev Challenges

    Microsoft officially launched its new dual-screen Android device, Surface Duo, presenting new challenges -- and opportunities -- for developers to leverage the new form factor.

  • What's New in Blazor Tooling Updates

    Here's a quick look at what four major third-party Blazor tooling vendors have offered lately for Microsoft's red-hot project that allows for web development with C# instead of JavaScript.

.NET Insight

Sign up for our newsletter.

Terms and Privacy Policy consent

I agree to this site's Privacy Policy.

Upcoming Events