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()andApply()
Lifecycle Methods:
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
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
IModulefor 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
IModuleand pass it to the base classOverride
Configure()to register services and componentsOverride
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 assemblyModule.AddWorkflowsFrom<T>(): Register all workflow definitions in the assemblyServices.Add...(): Access the service collection directly for custom registrationsModule.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.cs 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 valueCodeActivity<T>: For activities that produce a single output of typeTActivity: 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 designerDefaultValue: Default value if not specifiedUIHint: Custom UI control for the property editorCategory: 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:
SampleActivity.cs - A full custom activity with inputs and outputs
MyFeature.cs - A complete feature implementation
ModuleExtensions.cs - Extension methods following Elsa conventions
Best Practices
Follow Naming Conventions
Use
UseXyz()for feature extension methodsName features as
XyzFeatureUse clear, descriptive activity names
Provide Good Metadata
Use descriptive
[Activity]attributesAdd meaningful descriptions to inputs/outputs
Include usage examples in XML comments
Handle Errors Gracefully
Validate inputs in activities
Provide helpful error messages
Consider retry logic for transient failures
Test Thoroughly
Unit test activities independently
Integration test features
Test with the workflow designer
Document Your Extensions
Include XML documentation comments
Provide usage examples
Document configuration options
Further Reading
Custom Activities Guide - Detailed activity creation guide
Elsa Core Repository - Official source code and examples
Feature Documentation - Built-in features reference
Support
For questions and support:
Last updated