csharp-stateless

Create state machines in C# with Stateless .NET

  • 3 min

The Stateless library is an open-source tool for .NET that allows us to create state machines and workflows in a simple way.

State machines are a modeling methodology that is suitable for solving certain types of problems.

Stateless provides all the functionalities we typically need to generate state machines.

  • Generic states and triggers
  • State hierarchy
  • Actions on entering or exiting states
  • Transitions

In addition to many other non-”classical” ones, which can be useful in certain cases. Honestly, in this sense, it is a very, very complete library.

How to use Stateless .NET

To install “Stateless” in a .NET project, you can use the NuGet package manager. Open the NuGet Package Manager Console and run the command

Install-Package Stateless

Copied!

Here is a code example, taken from the library’s own documentation, which simply simulates an ON/OFF control

const string on = "On";
const string off = "Off";
const char space = ' ';

// Instantiate a new state machine in the 'off' state
var onOffSwitch = new StateMachine<string, char>(off);

onOffSwitch.Configure(off).Permit(space, on);
onOffSwitch.Configure(on).Permit(space, off);

Console.WriteLine("Press <space> to toggle the switch. Any other key will exit the program.");

while (true)
{
	Console.WriteLine("Switch is in state: " + onOffSwitch.State);
	var pressed = Console.ReadKey(true).KeyChar;
	
	// Check if user wants to exit
	if (pressed != space) break;

	// Use the Fire method with the trigger as payload to supply the state machine with an event.
	// The state machine will react according to its configuration.
	onOffSwitch.Fire(pressed);
}
Copied!

In this case, we have a state machine where the state is identified by a string, and the trigger by a char.

It only has two states, “On” and “Off”, and one trigger, ’ ’ (spacebar).

The ‘configure’ function is used to define the transitions from the On state to Off, and vice versa. Both, upon the ’ ’ trigger.

Finally, an infinite while loop is used, keyboard input is checked, and if it is a space, the transition is triggered with the Fire command.

This is a very simple and minimal example to show the functionality. For instance, a more complex one would be simulating the state of a bug tracking tool

private enum State { Open, Assigned, Deferred, Closed }

private enum Trigger { Assign, Defer, Close }

private readonly StateMachine<State, Trigger> _machine = new StateMachine<State, Trigger>(State.Open);

// ...

// Instantiate a new trigger with a parameter. 
_assignTrigger = _machine.SetTriggerParameters<string>(Trigger.Assign);

// Configure the Open state
_machine.Configure(State.Open)
	.Permit(Trigger.Assign, State.Assigned);

// Configure the Assigned state
_machine.Configure(State.Assigned)
	.SubstateOf(State.Open)
	.OnEntryFrom(_assignTrigger, OnAssigned)  // This is where the TriggerWithParameters is used. Note that the TriggerWithParameters object is used, not something from the enum
	.PermitReentry(Trigger.Assign)
	.Permit(Trigger.Close, State.Closed)
	.Permit(Trigger.Defer, State.Deferred)
	.OnExit(OnDeassigned);

// Configure the Deferred state
_machine.Configure(State.Deferred)
	.OnEntry(() => _assignee = null)
	.Permit(Trigger.Assign, State.Assigned);
Copied!

In this case, we are using enumerations for both the state and the trigger, which will be the normal way to work with the library.

Next, the transitions between states are defined, as well as the actions to perform when entering and exiting the nodes.

As we can see, it is a very complete library for defining state machines. It surely has all the functionalities you might ever need in your project. For more information, you can consult the library’s documentation and examples.