DSeries.DStateMachine.Core
1.0.1
See the version list below for details.
dotnet add package DSeries.DStateMachine.Core --version 1.0.1
NuGet\Install-Package DSeries.DStateMachine.Core -Version 1.0.1
<PackageReference Include="DSeries.DStateMachine.Core" Version="1.0.1" />
<PackageVersion Include="DSeries.DStateMachine.Core" Version="1.0.1" />
<PackageReference Include="DSeries.DStateMachine.Core" />
paket add DSeries.DStateMachine.Core --version 1.0.1
#r "nuget: DSeries.DStateMachine.Core, 1.0.1"
#addin nuget:?package=DSeries.DStateMachine.Core&version=1.0.1
#tool nuget:?package=DSeries.DStateMachine.Core&version=1.0.1
DStateMachine
DStateMachine is a powerful and flexible asynchronous state machine library for .NET, designed with a clean, fluent API and production-ready architecture. It supports dynamic transitions, guard conditions, entry/exit hooks, and internal transitions, making it ideal for complex stateful workflows.
✨ Features
- Generic Support: Works with any type for states and triggers (e.g.,
string
,int
,enum
). - Fluent API: Concise and expressive DSL for configuration.
- Asynchronous Execution: Seamless async/await support for transitions and actions.
- Entry and Exit Hooks: Configure entry/exit actions per state.
- Guard Clauses: Conditionally block transitions.
- Internal Transitions: Perform side-effect actions without state change.
- Dynamic Transitions: Determine the destination state at runtime.
- DOT Export: Generate DOT-format graphs for visualization.
📚 Example Usage
var sm = new DStateMachine<string, string>("A");
sm.Configure("A")
.OnEntry(() => Console.WriteLine("Entering A"))
.OnExit(() => Console.WriteLine("Exiting A"))
.OnTrigger("toB", tb => tb.ChangeState("B"));
sm.Configure("B").OnEntry(() => Console.WriteLine("Entered B"));
await sm.FireAsync("toB");
Console.WriteLine(sm.CurrentState); // Output: B
✅ Feature Examples
✅ Generic Type Support
var sm = new DStateMachine<int, int>(0);
sm.Configure(0).OnTrigger(1, tb => tb.ChangeState(2));
sm.Fire(1);
Console.WriteLine(sm.CurrentState); // Output: 2
🔁 Entry and Exit Actions
var sm = new DStateMachine<string, string>("Init");
bool entered = false, exited = false;
sm.Configure("Init")
.OnEntry(() => { entered = true; return Task.CompletedTask; })
.OnExit(() => { exited = true; return Task.CompletedTask; })
.OnTrigger("go", tb => tb.ChangeState("Done"));
sm.Configure("Done").OnEntry(() => Task.CompletedTask);
sm.Fire("go");
Console.WriteLine($"Entered: {entered}, Exited: {exited}"); // Output: Entered: False, Exited: True
⛔ Guard Clauses
var sm = new DStateMachine<string, string>("A");
sm.Configure("A")
.OnTrigger("toB", tb => tb.ChangeState("B").If(() => false));
sm.OnUnhandledTrigger((trigger, machine) => {
Console.WriteLine("Blocked by guard");
return Task.CompletedTask;
});
sm.Fire("toB"); // Output: Blocked by guard
⏳ Asynchronous Transitions
var sm = new DStateMachine<string, string>("Start");
sm.Configure("Start")
.OnTrigger("load", tb => tb.ChangeStateAsync(async () => {
await Task.Delay(100);
return "Loaded";
}));
sm.Configure("Loaded").OnEntry(() => Task.CompletedTask);
await sm.FireAsync("load");
Console.WriteLine(sm.CurrentState); // Output: Loaded
🧠 Dynamic Transitions
var sm = new DStateMachine<string, string>("A");
sm.Configure("A")
.OnTrigger("toNext", tb => tb.ChangeState(() => DateTime.Now.Second % 2 == 0 ? "Even" : "Odd"));
sm.Configure("Even").OnEntry(() => Task.CompletedTask);
sm.Configure("Odd").OnEntry(() => Task.CompletedTask);
sm.Fire("toNext");
Console.WriteLine(sm.CurrentState); // Output: "Even" or "Odd"
🔁 Internal Transitions
var sm = new DStateMachine<string, string>("Idle");
bool logged = false;
sm.Configure("Idle")
.OnTrigger("ping", tb => tb.ExecuteAction(() => logged = true));
await sm.FireAsync("ping");
Console.WriteLine($"State: {sm.CurrentState}, Logged: {logged}");
// Output: State: Idle, Logged: True
💬 Fluent DSL
var sm = new DStateMachine<string, string>("X");
sm.Configure("X")
.OnTrigger("a", tb => tb.ChangeState("A"))
.OnTrigger("b", tb => tb.ChangeState("B"));
Console.WriteLine(sm.Configure("X").Machine == sm); // Output: True
📈 DOT Graph Export
var sm = new DStateMachine<string, string>("Start");
sm.Configure("Start").OnTrigger("toEnd", tb => tb.ChangeState("End"));
sm.Configure("End").OnEntry(() => Task.CompletedTask);
string dot = sm.ExportToDot();
Console.WriteLine(dot);
// Output: DOT-format string of the state machine
🎓 Getting Started
- Clone the repository or add the files to your project.
- Create a new instance:
new DStateMachine<TTrigger, TState>(initialState)
. - Configure states using
.Configure(state)
and chainOnEntry
,OnExit
, andOnTrigger
. - Fire transitions using
Fire(trigger)
orawait FireAsync(trigger)
.
🙌 Contributions
Pull requests and issues are welcome! If you'd like to contribute improvements or new features, feel free to fork and open a PR.
📄 License
This project is licensed under the MIT License.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Release Notes Vesron 1.0.1:
Async Transition Enhancements:
Introduced asynchronous guards for transitions by converting existing synchronous guard methods to asynchronous (wrapped in Task.FromResult).
Added a new IfAsync method to support asynchronous guard conditions.
Updated all transition delegates (fixed, dynamic, and internal) to support asynchronous execution.
Async Action Support:
Added the ExecuteActionAsync method to allow asynchronous execution of side-effect actions during internal transitions.
Maintained backward compatibility by keeping the synchronous ExecuteAction method with wrapping.
These changes provide a flexible and robust asynchronous state machine framework while ensuring existing synchronous functionality remains intact.