Scott Hanselman

Stateless 3.0 - A State Machine library for .NET Core

November 11, 2016 Comment on this post [31] Posted in DotNetCore | Open Source
Sponsored By

.NET StandardState Machines and business processes that describe a series of states seem like they'll be easy to code but you'll eventually regret trying to do it yourself. Sure, you'll start with a boolean, then two, then you'll need to manage three states and there will be an invalid state to avoid then you'll just consider quitting all together. ;)

"Stateless" is a simple library for creating state machines in C# code. It's recently been updated to support .NET Core 1.0. They achieved this not by targeting .NET Core but by writing to the .NET Standard. Just like API levels in Android abstract away the many underlying versions of Android, .NET Standard is a set of APIs that all .NET platforms have to implement. Even better, the folks who wrote Stateless 3.0 targeted .NET Standard 1.0, which is the broadest and most compatible standard - it basically works everywhere and is portable across the .NET Framework on Windows, .NET Core on Windows, Mac, and LInux, as well as Windows Store apps and all phones.

Sure, there's Windows Workflow, but it may be overkill for some projects. In Nicholas Blumhardt's words:

...over time, the logic that decided which actions were allowed in each state, and what the state resulting from an action should be, grew into a tangle of if and switch. Inspired by Simple State Machine, I eventually refactored this out into a little state machine class that was configured declaratively: in this state, allow this trigger, transition to this other state, and so-on.

A state machine diagram describing the states a Bug can go throughYou can use state machines for anything. You can certainly describe high-level business state machines, but you can also easily model IoT device state, user interfaces, and more.

Even better, Stateless also serialize your state machine to a standard text-based "DOT Graph" format that can then be generated into an SVG or PNG like this with http://www.webgraphviz.com. It's super nice to be able to visualize state machines at runtime.

Modeling a Simple State Machine with Stateless

Let's look at a few code examples. You start by describing some finite states as an enum, and some finite "triggers" that cause a state to change. Like a switch could have On and Off as states and Toggle as a trigger.

A more useful example is the Bug Tracker included in the Stateless source on GitHub. To start with here are the states of a Bug and the Triggers that cause state to change:

enum State { Open, Assigned, Deferred, Resolved, Closed }
enum Trigger { Assign, Defer, Resolve, Close }

You then have your initial state, define your StateMachine, and if you like, you can pass Parameters when a state is trigger. For example, if a Bug is triggered with Assign you can pass in "Scott" so the bug goes into the Assigned state - assigned to Scott.

State _state = State.Open;
StateMachine<State, Trigger> _machine;
StateMachine<State, Trigger>.TriggerWithParameters<string> _assignTrigger;

string _title;
string _assignee;

Then, in this example, the Bug constructor describes the state machine using a fluent interface that reads rather nicely.

public Bug(string title)
{
_title = title;

_machine = new StateMachine<State, Trigger>(() => _state, s => _state = s);

_assignTrigger = _machine.SetTriggerParameters<string>(Trigger.Assign);

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

_machine.Configure(State.Assigned)
.SubstateOf(State.Open)
.OnEntryFrom(_assignTrigger, assignee => OnAssigned(assignee))
.PermitReentry(Trigger.Assign)
.Permit(Trigger.Close, State.Closed)
.Permit(Trigger.Defer, State.Deferred)
.OnExit(() => OnDeassigned());

_machine.Configure(State.Deferred)
.OnEntry(() => _assignee = null)
.Permit(Trigger.Assign, State.Assigned);
}

For example, when the State is Open, it can be Assigned. But as this is written (you can change it) you can't close a Bug that is Open but not Assigned. Make sense?

When the Bug is Assigned, you can Close it, Defer it, or Assign it again. That's PermitReentry(). Also, notice that Assigned is a Substate of Open.

You can have events that are fired as states change. Those events can take actions as you like.

void OnAssigned(string assignee)
{
if (_assignee != null && assignee != _assignee)
SendEmailToAssignee("Don't forget to help the new employee.");

_assignee = assignee;
SendEmailToAssignee("You own it.");
}

void OnDeassigned()
{
SendEmailToAssignee("You're off the hook.");
}

void SendEmailToAssignee(string message)
{
Console.WriteLine("{0}, RE {1}: {2}", _assignee, _title, message);
}

With a nice State Machine library like Stateless you can quickly model states that you'd ordinarily do with a "big ol' switch statement."

What have you used for state machines like this in your projects?


Sponsor: Big thanks to Telerik! They recently published a comprehensive whitepaper on The State of C#, discussing the history of C#, what’s new in C# 7 and whether C# is still a viable language. Check it out!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
November 11, 2016 11:17
This is suitable for simplifying the workflow
November 11, 2016 11:19
I like "One library to rule them all.."
November 11, 2016 11:25
Haven't used this but it seems a lot of configuration that you could otherwise properly encapsulate using the State design pattern.

But it's good to know what's on the market and that more and more of these libraries are migrating to netstandard.
November 11, 2016 11:43
I have a T4 template that generates state machines from a static definition (in an XML file, if I recall correctly - I don't have it to hand). In behaviour, the generated state machines would be similar to the ones generated by this library, including the firing of events for state transitions.

One thing my T4 template is missing compared to this is parameterised triggers. It doesn't have DOT graph output either, though that would be really easy to add.

The T4 might have the edge for when there are design changes to the states and transitions, as a lot of boiler-plate code will be rewritten automatically, whereas it looks like you'd have to do more of that by hand with the fluent API, but the T4 can't take care of it all and manual changes would still be required.
November 11, 2016 12:16
We use stateless with a custom "Visio like" designer and a factory to build at run time any user defined state machine. Works like a charm
November 11, 2016 13:19
Very practical and well explained. Nice to see that compared to other solutions you have the compiler backing you up.
November 11, 2016 13:52
Thanks for taking the time to write about the project! I was really pleasantly surprised to find that .NET Standard 1.0 was sufficient for it :-)
November 11, 2016 14:22
Thanks for showing this Scott, it is exactly what I am looking for on a current project.
November 11, 2016 14:37
It's nice to see articles about state machines. For state machines, we use XComponent. With XComponent you can create services fully designed using state machines.
November 11, 2016 15:04
Nice.

Fails though.

Beyond ~50 states, one does not model states at the object level.

One models large macro level states first as a macro level state engine, then decomposes each of the macro level states into many, many micro level states.

Each macro level state has a few states that transition into it and a few states transitioned to from the macro level state.

One models the X inputs/events in the system as a whole, then builds a table for each state/input set combination with the action to take and next state to transition to.

This is well known from at least the mid 1960s. Refer to Wikipedia for Decision Table, filetab or table based programming languages.

The failure of listing states inside the object as opposed to a tabular (input value combination, current state, action to take, next state) approach does not let one validate that the states
- Meet business rules / follow requirements - business users can read a table but not C# code
- Each state and possible combination of inputs has an action and next state -> No un-handled input combinations or states

The table is considerably smaller than the possible input combinations as most of the entries in the table will be "don't care" or "not possible."

Using a macro level states in one table for macro level operations; and an individual table for the micro level states in a particular macro level state greatly simplifies the complexity.

Modularity is achieved in that a macro level state encapsulates most of the content/settings/values/logic needed by its internal micro level states.

Developer test case:
- Extract the business rules/logic from c# code for a project using the state engine mentioned in this blog post.

Consider the time/cost needed to extract and present to business users the states/business logic behind a system using the state engine library with 500+ objects and 2000+ states.
Ted
November 11, 2016 15:14
p.s. Used the macro state/micro state for real-time hardware multi-device driver project running in OS memory space. Needed full validation that all combinations of asynchronous/synchronous events were handled and would not crash the machine.

Developed in C, with almost no pointers, internal to my process (not CRT) memory handling (think bounds checking) and stack checking

C++/OO was ruled out for memory handling and slower performance.
Ted
November 11, 2016 17:55
Ted, I would say that if you have objects with more than 50 states you should consider a different design principle.
November 11, 2016 22:05
Stateless usually, but we like very much StaMa as well.
November 12, 2016 2:50
Henning:

Please suggest an alternate design principle.

Real time asynchronous systems suited to state engines to prove all input combinations handled for each state.

Real-time inside the OS address space means you are on your own and cannot drop in any random third party library.

A top level 15 states where each top level state has 20 states is manageable.

Ted
November 12, 2016 3:00
Stateless is a handy little library, but one thing may strike you immediately: When you look at the state machine diagram it is very intuitive (easy to understand), but when you look at the Stateless C# definition...it's, well, let's just say opaque. You need to look closely and think it through to understand what that state machine does. Once you start to build a Stateless SM with more than a few states, the code definition becomes increasingly harder to reason about and refactor, and also test.

This to me is the fundamental limitation of Stateless and all code-based SM definitions. SM inherently lend themselves to diagrams, and the only reason tools do not exist to model them as such is because building such tools is bloody hard! Many attempts have been made over the decadess, and there is even one for Stateless (statelessdesigner.codeplex.com), but they almost always fall short.

But recently we have discovered an open-source product called YAKINDU Statecharts (www.statecharts.org), which I believe is the first visual-modelling tool I have looked at which is impressive, mature, documented, and under active development. It allows you to simulate execution of the state machines (amazing feature!) which really helps in testing and reasoning about your state machines. To use the state machine you then generate code in your desired language. I think the pro edition even includes a unit testing feature (Which, if you have ever tried to unit test a Stateless SM, will know it can be very boring activity - not suited to a procedural language like C#).

The only problem with YAKINDU is lack of C# support. But this feature is currently in development, and if the .NET community showed interest they might push this up their priorities. They take pull requests and am sure they would love to hear from this community.

Anyway, check out YAKINDU - I think it would be an awesome tool to add to the .NET arsenal.
November 12, 2016 3:42
Developed one in 1997 in VB that used a state matrix (even though it was sparse). It was used to get CA a quick fix to emulate a product that they couldn't get out the door. With FSM, it was operational in 4 weeks.

I wrote a visual, lightweight FSM in C# where state interactions are associated with function calls via drag & drop and the state at any time is immutable. It was fun and allowed for a sandboxed FSM, which acts like a state, whose innards are sealed but can be accessed like any other state. The objective was to write a framework for medical devices that would allow separate sensor/action components to be FDA certified allowing to create an end assembly of these components with a faster track to FDA certification...more work intensive that building a device by itself.
November 12, 2016 19:54
An interesting 'roll-your-own' approach is to encode states at the type level and encode transitions as as simply functions from one state to the next, but with the added twist that the function signatures specify exactly which transitions are valid, and prevent invalid transitions at compile time.

So the compile will fail with a type mismatch error if you try to do something like
Bug.close (Bug.open 1)


Here is a sample in F#, it can probably be ported to C# without too much hassle:


(* We want to capture states at the type level. *)
module State =
type open = Open
type assigned = Assigned of string
type deferred = Deferred
type resolved = Resolved
type closed = Closed

module Bug =
(*
We make the record implementation private so users can't just create
bugs in any state they like.
*)
type 'state t = private { id' : int; state : 'state }

let open (id' : int) : State.open t =
{ id' = id'; state = State.Open }

let assign (t : State.open t) (to : string) : State.assigned t =
{ t with state = State.Assigned to }

let close (t : State.assigned t) : State.closed t =
{ t with state = State.Closed }

let defer (t : State.assigned t) : State.deferred t =
{ t with state = State.Deferred }

let reassign (t : State.assigned t) (to : string) : State.assigned t =
{ t with state = State.Assigned to }


Apologies for the weird formatting, not sure why it's doing that.
November 14, 2016 10:02
Real-time, asynchronous, multi-sensor inputs and the possibility of faulty inputs due to a malfunctioning sensor leads to graph based and table based state machines.

A benchmark is to write a sliding window packet based file transmission protocol and prove that it handles all scenarios of timeout, bad packets, out of order packets, and disconnect at any time. Try this with while loops, if/then statements first, get bogged down, back up and then create a state diagram; and finally convert the state diagram to a table.

Hint, collapse a few of the adjacent states into very simple if/then logic.


Ted
November 14, 2016 18:46
If I am reading the sample code correctly, it looks as if Stateless also supports Hierarchical State Machines (although the DOT graph representation doesn't demonstrate it). This is an important convention if you want to avoid redundancy and complexity as the state machine scales up to more sophisticated models.
Am I reading it correctly that "SubstateOf" is intended for such hierarchical purposes?
November 14, 2016 23:39
Well, back in the days of the Honeywell 66 we used state table parsing to ndecode and act upon user input. Of course that was before GUI's and pretty much all input was text entered at a clunky terminal connected to the mainframe by acoustic couplers.

Look back to 1970 and see if you can find the paper by A. C. Day which explained the technique.

Mike L
November 15, 2016 4:04
@Kevin Mote yes, `SubstateOf()` creates HSM state hierarchies.
November 15, 2016 5:32
What have I used a Statemachine for?

Implementing the behaviour I need when someone logs on to a website...

InState(UnknownUserState)
.OnEntry()
.Then((state, evnt) =>
{
state.ResetUserLoginDetails();
});

InState(MasterState)
.When<UserLoggedInWithoutKnownCustomerEvent>()
.Then((state, evnt) =>
{
state.LogUserInToChooseCustomer(evnt.CustomerName);
})
.TransitionTo(WaitingForCustomerNumberState);


InState(MasterState)
.When<UserLoggedInEvent>()
.Then((state, evnt) =>
{
state.UserLogIn(evnt.UserName, evnt.CustomerId);
})
.TransitionTo(OkayState);

InState(MasterState)
.When<NewUserSessionStartedEvent>()
.TransitionTo(UnknownUserState);

November 15, 2016 10:39
Wonderful!

Below is how we can change the state of a bug...


// Assign to me ;-)
_machine.Fire(_assignTrigger, "Mihir");
// Mark it resolved
_machine.Fire(Trigger.Resolve);
// Close it
_machine.Fire(Trigger.Close);
November 15, 2016 18:15
Sounds useful.
So it "works on any phone" - including a Lumia 920 (Windows 8.1) ?
November 16, 2016 5:19
Hey, these state machines are perfect for game programming.

Are there execution times? Like for example, time to create 1000 Bug Objects, and time to change the state of 10000 Bug objects...

(In case I decide to be lazy and put this instead of a proper State Design Pattern.)
November 16, 2016 8:15
Nice! I can imagine using this for a Sharepoint workflow project and how easy it would have made my life back then.
November 21, 2016 7:09

For those interested in state machines for game development take a look at the open source lib we made at RSG:

https://github.com/Real-Serious-Games/Fluent-State-Machine

We use it for game states and logic, structuring UI state and simple AI.
November 21, 2016 20:53
Library looks good.Any reasons for using this over Windows Workflow Foundation other than simplicity? Anyone tried comparing with WWF?
Joy
November 23, 2016 1:15
Maybe it's just me, but the code samples don't show well in IE 11, look fine in Chrome. In IE 11, all code is just one line, requiring horizontal scrolling to see all the code.

In other new, great article! I'm going to use this in my business app I'm developing at the moment - perfect for managing states in a workflow. I'm also thinking it could help with a simple business rules engine, perhaps by using states to signify business rule issues (warnings, errors, etc.).
November 23, 2016 7:55
Very long back i used the open source state machine compiler with asp.net Now i see very similar thing is packaged in .net. Good to see this. Thanks for sharing.
http://www.codeproject.com/Articles/30305/State-Machine-Complier-And-Asp-net
December 04, 2016 2:40
What im missing in Stateless is the "Do" action of each state (like in Rob Lancasters alternative sample above)
Especially for longer running (async) tasks, versus the requirement to lock access to the state machine when making transitions.

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.