FluentSeq provides a fluent interface for creating easy-to-read sequences, eliminating the need for lengthy if-else statements. Successor of IegTools.Sequencer - new architecture, state-based instead of transaction-based
$ dotnet add package FluentSeqFluentSeq is the successor of IegTools.Sequencer.
FluentSeq provides a fluent interface for creating easy-to-read sequences,
eliminating the need for lengthy if-else statements.
The library is written in C# 13 and multi-targets
The library allows you to configure:
The Test coverage for .NET 8.0, .NET 9.0 and .NET 10.0 is greater than 95%
Installation
Usage
States
Trigger
Actions
Validation
Version Changes
Breaking Changes
Preview next Version v2.0
The library is available as a NuGet package.
A simple example configuration and usage for an OnTimer-sequence as xUnit-Test:
using FluentSeq.Builder;
using FluentSeq.Core;
public sealed class OnTimerCreateExampleTests
{
private ISequence<TimerState>? _sequence;
private bool _onTimerInput;
private ISequenceBuilder<TimerState> GetOnTimerConfiguration(int dwellTimeInMs) =>
new FluentSeq<TimerState>().Create(TimerState.Off)
.ConfigureState(TimerState.Off)
.TriggeredBy(() => !_onTimerInput)
.ConfigureState(TimerState.Pending)
.TriggeredBy(() => _onTimerInput)
.WhenInState(TimerState.Off)
.ConfigureState(TimerState.On)
.TriggeredBy(() => _onTimerInput)
.WhenInState(TimerState.Pending, () => TimeSpan.FromMilliseconds(dwellTimeInMs))
.Builder();
[Theory]
[InlineData(false, 9, 0, TimerState.Off, TimerState.Off)]
[InlineData(false, 9, 0, TimerState.Pending, TimerState.Off)]
[InlineData(false, 9, 0, TimerState.On, TimerState.Off)]
[InlineData(false, 1, 2, TimerState.Off, TimerState.Off)]
[InlineData(false, 1, 2, TimerState.Pending, TimerState.Off)]
[InlineData(false, 1, 2, TimerState.On, TimerState.Off)]
[InlineData(true, 9, 0, TimerState.Off, TimerState.Pending)]
[InlineData(true, 9, 0, TimerState.Pending, TimerState.Pending)]
[InlineData(true, 9, 0, TimerState.On, TimerState.On)]
[InlineData(true, 1, 2, TimerState.Off, TimerState.Pending)]
[InlineData(true, 1, 2, TimerState.Pending, TimerState.On)]
[InlineData(true, 1, 2, TimerState.On, TimerState.On)]
public async Task Example_Usage_OnTimerConfiguration_Run_async(bool timerInput, int dwellTimeInMs, int sleepTimeInMs, TimerState currentState, TimerState expectedState)
{
_sequence = GetOnTimerConfiguration(dwellTimeInMs).Build();
_onTimerInput = timerInput;
_sequence.SetState(currentState);
await Task.Delay(sleepTimeInMs);
await _sequence.RunAsync();
var actual = _sequence.CurrentState;
actual.ShouldBe(expectedState);
}
}A simple example configuration and usage for an OffTimer-sequence as xUnit-Test:
using FluentSeq.Builder;
using FluentSeq.Core;
public sealed class OffTimerConfigureExampleTests
{
private ISequence<TimerState>? _sequence;
private bool _onTimerInput;
private ISequenceBuilder<TimerState> GetOffTimerConfiguration(int dwellTimeInMs) =>
new FluentSeq<TimerState>().Configure(TimerState.Off, builder =>
{
builder.ConfigureState(TimerState.On)
.TriggeredBy(() => _onTimerInput);
builder.ConfigureState(TimerState.Pending)
.TriggeredBy(() => !_onTimerInput)
.WhenInState(TimerState.On);
builder.ConfigureState(TimerState.Off)
.TriggeredBy(() => !_onTimerInput)
.WhenInState(TimerState.Pending, () => TimeSpan.FromMilliseconds(dwellTimeInMs));
}).Builder();
[Theory]
[InlineData(true, 9, 0, TimerState.Off, TimerState.On)]
[InlineData(true, 9, 0, TimerState.Pending, TimerState.On)]
[InlineData(true, 9, 0, TimerState.On, TimerState.On)]
[InlineData(true, 1, 2, TimerState.Off, TimerState.On)]
[InlineData(true, 1, 2, TimerState.Pending, TimerState.On)]
[InlineData(true, 1, 2, TimerState.On, TimerState.On)]
[InlineData(false, 9, 0, TimerState.Off, TimerState.Off)]
[InlineData(false, 9, 0, TimerState.Pending, TimerState.Pending)]
[InlineData(false, 9, 0, TimerState.On, TimerState.Pending)]
[InlineData(false, 1, 2, TimerState.Off, TimerState.Off)]
[InlineData(false, 1, 2, TimerState.Pending, TimerState.Off)]
[InlineData(false, 1, 2, TimerState.On, TimerState.Pending)]
public async Task Example_Usage_OffTimerConfiguration_Run_async(bool timerInput, int dwellTimeInMs, int sleepTimeInMs, TimerState currentState, TimerState expectedState)
{
_sequence = GetOffTimerConfiguration(dwellTimeInMs).Build();
_onTimerInput = timerInput;
_sequence.SetState(currentState);
await Task.Delay(sleepTimeInMs);
await _sequence.RunAsync();
var actual = _sequence.CurrentState;
actual.ShouldBe(expectedState);
}
}For more examples -> IntegrationTestsFluentSeq/Examples
States can be defined as strings, enums, int, objects, and so on.
Internally they will be stored as type SeqState.
var builder = new FluentSeq<TimerState>().Create(TimerState.Off)
.ConfigureState(TimerState.Off)
.Builder()
// or
var builder = new FluentSeq<string>().Create("Off")
.ConfigureState("Off")
.Builder()
TBD
TBD
The validation process ensures the sequence configuration is complete and adheres to the defined principles.
By default, validation is enabled but can be disabled either entirely or for specific states.
The sequence is validated during the build process.
To build a sequence:
_sequence = builder.Build();
Validation Principles:
Validation could be disabled:
var builder = new FluentSeq<TimerState>().Create(TimerState.Off)
.DisableValidation()
.ConfigureState(TimerState.Off)
.Builder() var builder = new FluentSeq<TimerState>().Create(Enum.Off)
.DisableValidationForStates(Enum.State1, Enum.State2, ...)
.ConfigureState(Enum.Off)
.Builder()