Skip to content

Basic StateMachine (Intro)

Damian edited this page Jan 10, 2026 · 2 revisions

Creating states can be as easy or as complex as you intend it to be. Using Lite.StateMachine follows to KISS principals so that you can quickly execute your operation(s) and move along, while offering flexibility in the framework for your unique projects.

Let's start with a basic state machine, and explain the parts as we go.

Basic State Machine

public enum StateId
{
  State1,
  State2,
  State3,
}

var machine = new StateMachine();
machine.RegisterState(StateId.State1, StateId.State2);
machine.RegisterState(StateId.State2, StateId.State3);
machine.RegisterState(StateId.State3);

await machine.RunAsync();

Overview

Types of States

Name Type Description
[State IState Basic state that can perform an operation or just move along.
Composite IState A "parent" state which contains sub-states. The states can be any of these 3 types of states.
Command ICommand A state which sends a message and expects a response. The response arrives in the OnMessage method. If it is not received in time (default 3 seconds), it will trigger the OnTimeout method.

State Result

Result.Ok;
Result.Error;
Result.Failure;

Context - Preload With Parameters

As of v2.3, you can use AddContext(...) method to pre-load Properties and Errors into the Context. The design change removed the ability to pass in the PropertyBag parameters via the RunAsync(..) method.

v2.2.1 and before, use, RunAsync(TStateId, properties, errors); for passing the data into the Context.

// Context properties that you want to start out with
var ctxProperties = new PropertyBag()
{
  { ParameterType.Counter, 0 },
  { ParameterType.TestExecutionOrder, true },
};

var machine = await new StateMachine<StateId>()
  .RegisterState<BasicState1>(StateId.State1, StateId.State2)
  .RegisterState<BasicState2>(StateId.State2, StateId.State3)
  .RegisterState<BasicState3>(StateId.State3)
  .AddContext(ctxProperties)
  .RunAsync(BasicStateId.State1, cancellationToken: TestContext.CancellationToken);

Dependency Injection

The Lite.StateMachine offers the flexibility for any Dependency Injection (DI) framework to plug-into it, so that you can consume other classes/services via each state's constructor.

Most notable container systems include:

  • Microsoft.Extensions.DependencyInjection
  • DryIoc
  • AutoFac
  • (and more!)

Need help picking a container, check out these statistics (source)!

Method Mean Error StdDev Median
DryIoc 65.70 us 1.309 us 2.553 us 64.46 us
DryIoc_MsDI 97.96 us 1.959 us 4.382 us 96.59 us
MsDI 81.25 us 1.624 us 4.686 us 82.73 us
Autofac 323.50 us 6.408 us 8.555 us 320.06 us
Autofac_MsDI 367.96 us 7.324 us 14.111 us 362.58 us
Lamar_MsDI 3,643.30 us 56.678 us 55.666 us 3,630.33 us
Grace 13,870.26 us 282.593 us 824.337 us 13,837.66 us
Grace_MsDI 17,079.41 us 318.034 us 326.598 us 17,025.77 us

Default Transition Timeouts

Name Description
DefaultStateTimeoutMs Default value Timeout.Infinite
DefaultCommandTimeoutMs Default 3000 ms (3 sec)

Context

The Context is used for passing data from one state to the next. The Context has 2 core properties Parameters and ErrorStack which the user can populate and carry along to the other states for passing data.

The StateMachine has a property named, IsContextPersisted, which determines the lifetime of the parameters (default = true). Meaning, whether or not the keys will be garbage collected by StateMachine after transiting through the hierarchy of Composite states. When disabled (false), keys added to Parameters and ErrorStack during Composite state's OnEntering will remain for the lifetime of that level's hierarchy.

Read more about how the Context works in its own section.

Event Aggregator

The Event Aggregator is used by the Command states as a built-in mechanism for passing messages internally to the application.

Read more about the Event Aggregator for how to use it and its use-cases.

Clone this wiki locally