githubEdit

Plugins & Modules

Complete guide to extending Elsa Workflows with custom modules, features, and activities. Learn how to create reusable plugins and distribute them as NuGet packages.

Elsa Workflows provides a powerful and flexible extensibility system that allows you to create custom modules, features, and activities tailored to your specific needs. This guide will teach you how to extend Elsa with your own functionality and package it for reuse across projects.

Overview

The Elsa extensibility model is built around three core concepts:

  • Modules: Containers that group related features and provide a unified configuration interface

  • Features: Self-contained units of functionality that register services, activities, and other components

  • Activities: The building blocks of workflows that encapsulate specific actions or operations

This architecture enables you to:

  • Create domain-specific activities that encapsulate business logic

  • Package and distribute reusable extensions as NuGet packages

  • Maintain clean separation of concerns in large applications

  • Configure complex functionality through simple, fluent APIs

Table of Contents

Key Concepts

Modules & Features

Elsa uses a hierarchical configuration system where modules contain features, and features register the actual services and components.

IModule Interface

The IModule interface represents a container for features. The Elsa configuration system provides a default module implementation that you'll typically work with through extension methods.

FeatureBase Class

FeatureBase is the base class for all features in Elsa. A feature:

  • Encapsulates related functionality

  • Registers services with dependency injection

  • Can configure workflow options

  • Follows a two-phase initialization: Configure() and Apply()

Lifecycle Methods:

  1. Configure(): Called during application startup to register services and configure options. This is where you:

    • Register activities using AddActivitiesFrom<T>()

    • Register workflows using AddWorkflowsFrom<T>()

    • Add custom services to the DI container

    • Configure workflow options

  2. Apply(): Called after all features have been configured. Use this for:

    • Post-configuration tasks that depend on other features

    • Final validation

    • Complex initialization logic

UseXyz() Pattern

Elsa follows a convention where features are enabled using UseXyz() extension methods. This pattern:

  • Provides a fluent, discoverable API

  • Allows optional configuration via lambda expressions

  • Returns IModule for method chaining

Example:

Activity Discovery & Registration

Elsa provides several methods for registering activities and workflows:

AddActivitiesFrom()

Scans the assembly containing type T and registers all classes marked with the [Activity] attribute:

This method:

  • Discovers all activity classes in the assembly

  • Registers them with the activity registry

  • Makes them available in the workflow designer

AddWorkflowsFrom()

Similar to AddActivitiesFrom<T>(), but registers workflow definitions:

Creating a Custom Feature

Let's walk through creating a custom feature step by step.

Step 1: Define Your Feature Class

Create a class that inherits from FeatureBase:

Key Points:

  • The constructor must accept IModule and pass it to the base class

  • Override Configure() to register services and components

  • Override Apply() only if you need post-configuration logic

Step 2: Configure Services

Inside the Configure() method, register your activities and services:

Available Registration Methods:

  • Module.AddActivitiesFrom<T>(): Register all activities in the assembly

  • Module.AddWorkflowsFrom<T>(): Register all workflow definitions in the assembly

  • Services.Add...(): Access the service collection directly for custom registrations

  • Module.ConfigureWorkflowOptions(): Configure workflow-specific settings

Step 3: Create Extension Methods

Create a static extension class with UseXyz() methods following the Elsa convention:

Pattern with Options:

For more complex configuration, use an options class:

Step 4: Register Your Feature

In your application's Program.cs or Startup.cs, use your extension method:

Creating Custom Activities

Custom activities are the primary way to extend workflow functionality. See examples/SampleActivity.csarrow-up-right for a complete example.

Basic Activity Structure

Activities inherit from CodeActivity or CodeActivity<T> (for activities with outputs):

Base Classes:

  • CodeActivity: For activities without a return value

  • CodeActivity<T>: For activities that produce a single output of type T

  • Activity: For more complex activities with custom behavior

Defining Inputs and Outputs

Use the [Input] and [Output] attributes to define activity ports:

Input/Output Features:

  • Description: Shown in the workflow designer

  • DefaultValue: Default value if not specified

  • UIHint: Custom UI control for the property editor

  • Category: Groups related properties in the designer

Activity Attributes

The [Activity] attribute configures how the activity appears in the designer:

Attribute Parameters:

  • Namespace: Groups activities logically (e.g., "MyCompany.Integration")

  • Category: Organizes activities in the designer toolbox

  • Description: Provides help text for workflow designers

  • DisplayName: Overrides the class name in the designer

Registering Activities

Activities are registered via features:

This scans for all types marked with [Activity] and registers them with the activity registry. They become immediately available in:

  • The workflow designer

  • Programmatic workflow definitions

  • The workflow execution engine

Packaging & Distribution

To share your custom modules, package them as NuGet packages:

1. Create a Class Library Project

2. Organize Your Code

3. Configure the .csproj File

4. Build and Publish

5. Consume the Package

Users can then install and use your package:

Advanced Topics

Custom UI Hint Handlers

UI hint handlers control how activity properties are edited in the workflow designer:

Register in your feature:

Custom Serializers

For complex data types, implement custom serializers:

Register in your feature:

Activity Execution Context

The ActivityExecutionContext provides access to:

  • Workflow Instance: Current workflow state and variables

  • Input/Output: Get and set activity inputs and outputs

  • Journal: Log custom data for debugging

  • Cancellation: Handle workflow cancellation

  • Services: Access dependency injection container

Complete Examples

For complete, working examples, see:

Best Practices

  1. Follow Naming Conventions

    • Use UseXyz() for feature extension methods

    • Name features as XyzFeature

    • Use clear, descriptive activity names

  2. Provide Good Metadata

    • Use descriptive [Activity] attributes

    • Add meaningful descriptions to inputs/outputs

    • Include usage examples in XML comments

  3. Handle Errors Gracefully

    • Validate inputs in activities

    • Provide helpful error messages

    • Consider retry logic for transient failures

  4. Test Thoroughly

    • Unit test activities independently

    • Integration test features

    • Test with the workflow designer

  5. Document Your Extensions

    • Include XML documentation comments

    • Provide usage examples

    • Document configuration options

Further Reading

Support

For questions and support:

Last updated