Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<!--<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>-->
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="DryIoc" Version="5.4.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.0.0-1.25277.114" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.1" />
Expand Down
4 changes: 4 additions & 0 deletions Lite.StateMachine.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
<Project Path="samples/Sample01.BasicStates/Sample01.BasicStates.csproj" Id="07016e87-d49b-4ece-9d24-e062eaa02dc4" />
<Project Path="samples/Sample02.PassingParams/Sample02.PassingParams.csproj" Id="88e1e0a9-34b8-4610-a2f4-bfb36e0d1b43" />
<Project Path="samples/Sample03.DependencyInjection/Sample03.DependencyInjection.csproj" Id="f7481ec8-d3be-4acd-9eae-a4723c09e245" />
<Project Path="samples/Sample04.ResultTransitions/Sample04.ResultTransitions.csproj" Id="c1a4fb9a-6b1b-43e7-aeb1-2c07024b15d5" />
<Project Path="samples/Sample05.CommandStates/Sample05.CommandStates.csproj" Id="cd6b33b8-ac4a-4ec7-a3a8-5cdf14c03847" />
<Project Path="samples/Sample06.PassingEvents/Sample06.PassingEvents.csproj" Id="01a1903a-9da0-4cd2-a811-356822569e03" />
<Project Path="samples/Sample10.UsingEventIPC/Sample10.UsingEventIPC.csproj" Id="f89325bb-17ff-4db9-9647-92bad4e2f974" />
</Folder>
<Folder Name="/Solution Items/">
<File Path=".github/workflows/build-github.yml" />
Expand Down
29 changes: 0 additions & 29 deletions samples/Sample03.DependencyInjection/BasicDiStates.cs

This file was deleted.

3 changes: 2 additions & 1 deletion samples/Sample03.DependencyInjection/CounterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using Sample03.DependencyInjection.MsDI;

namespace Sample03.DependencyInjection;

Expand All @@ -10,7 +11,7 @@ public interface ICounterService
{
/// <summary
/// Gets or sets a counter.
/// <see cref="StateDiBase{TStateClass, TStateId}"/> uses it as an automatic state transition counter.
/// <see cref="BaseDiState{TStateClass, TStateId}"/> uses it as an automatic state transition counter.
/// </summary>
int Counter1 { get; set; }

Expand Down
148 changes: 148 additions & 0 deletions samples/Sample03.DependencyInjection/DryIocDI/DryIocStateMachine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright Xeno Innovations, Inc. 2026
// See the LICENSE file in the project root for more information.

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using DryIoc;
using Lite.StateMachine;
using Microsoft.Extensions.Logging;

namespace Sample03.DependencyInjection.DryIocDI;

#pragma warning disable SA1649 // File name should match first type name
#pragma warning disable SA1402 // File may only contain a single type

public static class DryIocStateMachine
{
/// <summary>State definitions.</summary>
public enum BasicStateId
{
State1,
State2,
State3,
}

public static async Task RunAsync()
{
var container = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments));

// Register Services
container.Register<IEventAggregator, EventAggregator>(Reuse.Singleton);
container.Register<ICounterService, CounterService>(Reuse.Singleton);

// Register Logger
//// For use with NLog:
//// var loggerFactory = new NLogLoggerFactory();
container.RegisterInstance<ILoggerFactory>(LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Trace);
builder.AddSimpleConsole(options =>
{
options.IncludeScopes = true;
options.SingleLine = true;
});
}));
container.Register(typeof(ILogger<>), typeof(Logger<>), Reuse.Transient);

// Register States
container.Register<State1>(Reuse.Transient);
container.Register<State2>(Reuse.Transient);
container.Register<State3>(Reuse.Transient);

// Resolve dependency services for post-run evaluations
Func<Type, object?> factory = t => container.Resolve(t);
var aggregator = container.Resolve<IEventAggregator>();
var counterService = container.Resolve<ICounterService>();

// Create State Machine
var machine = new StateMachine<BasicStateId>(factory)
.RegisterState<State1>(BasicStateId.State1, BasicStateId.State2)
.RegisterState<State2>(BasicStateId.State2, BasicStateId.State3)
.RegisterState<State3>(BasicStateId.State3);

var result = await machine.RunAsync(BasicStateId.State1);

Console.WriteLine("Post Execution Validations:");
Console.WriteLine("---------------------------");

Console.WriteLine($"* Counter service Counter1: {counterService.Counter1} (expected 9)");

// Ensure all states are registered
var enums = Enum.GetValues<BasicStateId>().Cast<BasicStateId>();
Console.WriteLine($"* State Machine Counts: {machine.States.Count()}. State Enum Count: {enums.Count()}");
Console.WriteLine($"* All states registered: {enums.All(k => machine.States.Contains(k))}");

// Ensure they're registered in order
// Validates that States are registered for execution in the same order as the defined enums. StateId 1 => 2 => 3.
Console.WriteLine($"* State registered in order: {enums.SequenceEqual(machine.States)}");
}

public class State1(ICounterService msg, ILogger<State1> log)
: BaseDiState<State1, BasicStateId>(msg, log);

public class State2(ICounterService msg, ILogger<State2> log)
: BaseDiState<State2, BasicStateId>(msg, log);

public class State3(ICounterService msg, ILogger<State3> log)
: BaseDiState<State3, BasicStateId>(msg, log);
}

public class BaseDiState<TStateClass, TStateId>(ICounterService msg, ILogger<TStateClass> logger)
: IState<TStateId>
where TStateId : struct, Enum
{
private readonly ILogger<TStateClass> _logger = logger;
private readonly ICounterService _msgService = msg;

/// <summary>Gets or sets a value indicating whether output transitions for debugging tests.</summary>
public bool HasExtraLogging { get; set; } = false;

public ILogger<TStateClass> Log => _logger;

public ICounterService MessageService => _msgService;

#region Suppress CodeMaid Method Sorting

[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "ignore")]
public virtual Task OnEntering(Context<TStateId> context)
{
_msgService.Counter1++;
_logger.LogInformation("[OnEntering]");

if (HasExtraLogging)
Debug.WriteLine($"[{GetType().Name}] [OnEntering]");

return Task.CompletedTask;
}

#endregion Suppress CodeMaid Method Sorting

public virtual Task OnEnter(Context<TStateId> context)
{
_msgService.Counter1++;
_logger.LogInformation("[OnEnter] => OK");

if (HasExtraLogging)
Debug.WriteLine($"[{GetType().Name}] [OnEnter] => OK");

context.NextState(Result.Success);
return Task.CompletedTask;
}

public virtual Task OnExit(Context<TStateId> context)
{
_msgService.Counter1++;
_logger.LogInformation("[OnExit]");

if (HasExtraLogging)
Debug.WriteLine($"[{GetType().Name}] [OnExit]");

context.NextState(Result.Success);
return Task.CompletedTask;
}
}

#pragma warning restore SA1649 // File name should match first type name
#pragma warning restore SA1402 // File may only contain a single type
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
using Lite.StateMachine;
using Microsoft.Extensions.Logging;

namespace Sample03.DependencyInjection;
namespace Sample03.DependencyInjection.MsDI;

#pragma warning disable SA1124 // Do not use regions

public class StateDiBase<TStateClass, TStateId>(ICounterService msg, ILogger<TStateClass> logger) : IState<TStateId>
public class BaseDiState<TStateClass, TStateId>(ICounterService msg, ILogger<TStateClass> logger)
: IState<TStateId>
where TStateId : struct, Enum
{
private readonly ILogger<TStateClass> _logger = logger;
Expand Down
80 changes: 80 additions & 0 deletions samples/Sample03.DependencyInjection/MsDI/MsDIStateMachine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright Xeno Innovations, Inc. 2026
// See the LICENSE file in the project root for more information.

using System;
using System.Linq;
using System.Threading.Tasks;
using Lite.StateMachine;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Sample03.DependencyInjection.MsDI;

#pragma warning disable SA1649 // File name should match first type name
#pragma warning disable SA1402 // File may only contain a single type

public static class MsDIStateMachine
{
/// <summary>State definitions.</summary>
public enum BasicStateId
{
State1,
State2,
State3,
}

public static async Task RunAsync()
{
// Assemble with Dependency Injection
var services = new ServiceCollection()
//// Register Services
.AddLogging(b => b.AddSimpleConsole(options =>
{
options.SingleLine = true;
options.IncludeScopes = true;
}))
.AddSingleton<ICounterService, CounterService>()
//// Register States
.AddTransient<State1>()
.AddTransient<State2>()
.AddTransient<State3>()
.BuildServiceProvider();

Func<Type, object?> factory = t => ActivatorUtilities.CreateInstance(services, t);

var machine = new StateMachine<BasicStateId>(factory)
.RegisterState<State1>(BasicStateId.State1, BasicStateId.State2)
.RegisterState<State2>(BasicStateId.State2, BasicStateId.State3)
.RegisterState<State3>(BasicStateId.State3);

var result = await machine.RunAsync(BasicStateId.State1);

Console.WriteLine("Post Execution Validations:");
Console.WriteLine("---------------------------");

var counterService = services.GetRequiredService<ICounterService>();
Console.WriteLine($"* Counter service Counter1: {counterService.Counter1} (expected 9)");

// Ensure all states are registered
var enums = Enum.GetValues<BasicStateId>().Cast<BasicStateId>();
Console.WriteLine($"* State Machine Counts: {machine.States.Count()}. State Enum Count: {enums.Count()}");
Console.WriteLine($"* All states registered: {enums.All(k => machine.States.Contains(k))}");

// Ensure they're registered in order
// Validates that States are registered for execution in the same order as the defined enums. StateId 1 => 2 => 3.
Console.WriteLine($"* State registered in order: {enums.SequenceEqual(machine.States)}");
}

public class State1(ICounterService msg, ILogger<State1> log)
: BaseDiState<State1, BasicStateId>(msg, log);

public class State2(ICounterService msg, ILogger<State2> log)
: BaseDiState<State2, BasicStateId>(msg, log);

public class State3(ICounterService msg, ILogger<State3> log)
: BaseDiState<State3, BasicStateId>(msg, log);
}

#pragma warning restore SA1649 // File name should match first type name
#pragma warning restore SA1402 // File may only contain a single type
46 changes: 4 additions & 42 deletions samples/Sample03.DependencyInjection/Program.cs
Original file line number Diff line number Diff line change
@@ -1,57 +1,19 @@
// Copyright Xeno Innovations, Inc. 2025
// Copyright Xeno Innovations, Inc. 2026
// See the LICENSE file in the project root for more information.

using System;
using System.Linq;
using System.Threading.Tasks;
using Lite.StateMachine;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Sample03.DependencyInjection;

internal class Program
{
private static async Task Main()
{
await TestMicrosoftDependencyInjectionAsync();
}

private static async Task TestMicrosoftDependencyInjectionAsync()
{
// Assemble with Dependency Injection
var services = new ServiceCollection()
//// Register Services
.AddLogging(b => b.AddSimpleConsole())
.AddSingleton<ICounterService, CounterService>()
//// Register States
.AddTransient<BasicDiState1>()
.AddTransient<BasicDiState2>()
.AddTransient<BasicDiState3>()
.BuildServiceProvider();

Func<Type, object?> factory = t => ActivatorUtilities.CreateInstance(services, t);

var machine = new StateMachine<BasicStateId>(factory)
.RegisterState<BasicDiState1>(BasicStateId.State1, BasicStateId.State2)
.RegisterState<BasicDiState2>(BasicStateId.State2, BasicStateId.State3)
.RegisterState<BasicDiState3>(BasicStateId.State3);

var result = await machine.RunAsync(BasicStateId.State1);

Console.WriteLine("\n\nPost Execution Validations:");
Console.WriteLine("---------------------------");

var msgService = services.GetRequiredService<ICounterService>();
Console.WriteLine($"* Message service Counter1: {msgService.Counter1} (expected 9)");
await DryIocDI.DryIocStateMachine.RunAsync();

// Ensure all states are registered
var enums = Enum.GetValues<BasicStateId>().Cast<BasicStateId>();
Console.WriteLine($"* State Machine Counts: {machine.States.Count()}. State Enum Count: {enums.Count()}");
Console.WriteLine($"* All states registered: {enums.All(k => machine.States.Contains(k))}");
Console.Write("\n-=-=-=-=-=-=-\n\n");

// Ensure they're registered in order
// Validates that States are registered for execution in the same order as the defined enums. StateId 1 => 2 => 3.
Console.WriteLine($"* State registered in order: {enums.SequenceEqual(machine.States)}");
await MsDI.MsDIStateMachine.RunAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="DryIoc" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<ProjectReference Include="..\..\source\Lite.StateMachine\Lite.StateMachine.csproj" />
</ItemGroup>
Expand Down
12 changes: 12 additions & 0 deletions samples/Sample04.ResultTransitions/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright Xeno Innovations, Inc. 2026
// See the LICENSE file in the project root for more information.

namespace Sample04.ResultTransitions;

internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine("Coming Soon!");
}
}
Loading
Loading