diff --git a/Directory.Packages.props b/Directory.Packages.props index 2eb4914..b6c8bc4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,6 +4,7 @@ + diff --git a/Lite.StateMachine.slnx b/Lite.StateMachine.slnx index f736c75..a57be99 100644 --- a/Lite.StateMachine.slnx +++ b/Lite.StateMachine.slnx @@ -3,6 +3,10 @@ + + + + diff --git a/samples/Sample03.DependencyInjection/BasicDiStates.cs b/samples/Sample03.DependencyInjection/BasicDiStates.cs deleted file mode 100644 index 83ce830..0000000 --- a/samples/Sample03.DependencyInjection/BasicDiStates.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright Xeno Innovations, Inc. 2025 -// See the LICENSE file in the project root for more information. - -using Microsoft.Extensions.Logging; - -namespace Sample03.DependencyInjection; - -#pragma warning disable SA1649 // File name should match first type name -#pragma warning disable SA1402 // File may only contain a single type - -/// State definitions. -public enum BasicStateId -{ - State1, - State2, - State3, -} - -public class BasicDiState1(ICounterService msg, ILogger log) - : StateDiBase(msg, log); - -public class BasicDiState2(ICounterService msg, ILogger log) - : StateDiBase(msg, log); - -public class BasicDiState3(ICounterService msg, ILogger log) - : StateDiBase(msg, log); - -#pragma warning restore SA1649 // File name should match first type name -#pragma warning restore SA1402 // File may only contain a single type diff --git a/samples/Sample03.DependencyInjection/CounterService.cs b/samples/Sample03.DependencyInjection/CounterService.cs index 7517485..7d42100 100644 --- a/samples/Sample03.DependencyInjection/CounterService.cs +++ b/samples/Sample03.DependencyInjection/CounterService.cs @@ -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; @@ -10,7 +11,7 @@ public interface ICounterService { /// uses it as an automatic state transition counter. + /// uses it as an automatic state transition counter. /// int Counter1 { get; set; } diff --git a/samples/Sample03.DependencyInjection/DryIocDI/DryIocStateMachine.cs b/samples/Sample03.DependencyInjection/DryIocDI/DryIocStateMachine.cs new file mode 100644 index 0000000..c09de02 --- /dev/null +++ b/samples/Sample03.DependencyInjection/DryIocDI/DryIocStateMachine.cs @@ -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 +{ + /// State definitions. + public enum BasicStateId + { + State1, + State2, + State3, + } + + public static async Task RunAsync() + { + var container = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments)); + + // Register Services + container.Register(Reuse.Singleton); + container.Register(Reuse.Singleton); + + // Register Logger + //// For use with NLog: + //// var loggerFactory = new NLogLoggerFactory(); + container.RegisterInstance(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(Reuse.Transient); + container.Register(Reuse.Transient); + container.Register(Reuse.Transient); + + // Resolve dependency services for post-run evaluations + Func factory = t => container.Resolve(t); + var aggregator = container.Resolve(); + var counterService = container.Resolve(); + + // Create State Machine + var machine = new StateMachine(factory) + .RegisterState(BasicStateId.State1, BasicStateId.State2) + .RegisterState(BasicStateId.State2, BasicStateId.State3) + .RegisterState(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().Cast(); + 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 log) + : BaseDiState(msg, log); + + public class State2(ICounterService msg, ILogger log) + : BaseDiState(msg, log); + + public class State3(ICounterService msg, ILogger log) + : BaseDiState(msg, log); +} + +public class BaseDiState(ICounterService msg, ILogger logger) + : IState + where TStateId : struct, Enum +{ + private readonly ILogger _logger = logger; + private readonly ICounterService _msgService = msg; + + /// Gets or sets a value indicating whether output transitions for debugging tests. + public bool HasExtraLogging { get; set; } = false; + + public ILogger 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 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 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 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 diff --git a/samples/Sample03.DependencyInjection/StateDiBase.cs b/samples/Sample03.DependencyInjection/MsDI/BaseDiState.cs similarity index 91% rename from samples/Sample03.DependencyInjection/StateDiBase.cs rename to samples/Sample03.DependencyInjection/MsDI/BaseDiState.cs index 6cf9413..5df7ef8 100644 --- a/samples/Sample03.DependencyInjection/StateDiBase.cs +++ b/samples/Sample03.DependencyInjection/MsDI/BaseDiState.cs @@ -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(ICounterService msg, ILogger logger) : IState +public class BaseDiState(ICounterService msg, ILogger logger) + : IState where TStateId : struct, Enum { private readonly ILogger _logger = logger; diff --git a/samples/Sample03.DependencyInjection/MsDI/MsDIStateMachine.cs b/samples/Sample03.DependencyInjection/MsDI/MsDIStateMachine.cs new file mode 100644 index 0000000..f9754fe --- /dev/null +++ b/samples/Sample03.DependencyInjection/MsDI/MsDIStateMachine.cs @@ -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 +{ + /// State definitions. + 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() + //// Register States + .AddTransient() + .AddTransient() + .AddTransient() + .BuildServiceProvider(); + + Func factory = t => ActivatorUtilities.CreateInstance(services, t); + + var machine = new StateMachine(factory) + .RegisterState(BasicStateId.State1, BasicStateId.State2) + .RegisterState(BasicStateId.State2, BasicStateId.State3) + .RegisterState(BasicStateId.State3); + + var result = await machine.RunAsync(BasicStateId.State1); + + Console.WriteLine("Post Execution Validations:"); + Console.WriteLine("---------------------------"); + + var counterService = services.GetRequiredService(); + Console.WriteLine($"* Counter service Counter1: {counterService.Counter1} (expected 9)"); + + // Ensure all states are registered + var enums = Enum.GetValues().Cast(); + 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 log) + : BaseDiState(msg, log); + + public class State2(ICounterService msg, ILogger log) + : BaseDiState(msg, log); + + public class State3(ICounterService msg, ILogger log) + : BaseDiState(msg, log); +} + +#pragma warning restore SA1649 // File name should match first type name +#pragma warning restore SA1402 // File may only contain a single type diff --git a/samples/Sample03.DependencyInjection/Program.cs b/samples/Sample03.DependencyInjection/Program.cs index d4e8753..21f4a5c 100644 --- a/samples/Sample03.DependencyInjection/Program.cs +++ b/samples/Sample03.DependencyInjection/Program.cs @@ -1,12 +1,8 @@ -// 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; @@ -14,44 +10,10 @@ 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() - //// Register States - .AddTransient() - .AddTransient() - .AddTransient() - .BuildServiceProvider(); - - Func factory = t => ActivatorUtilities.CreateInstance(services, t); - - var machine = new StateMachine(factory) - .RegisterState(BasicStateId.State1, BasicStateId.State2) - .RegisterState(BasicStateId.State2, BasicStateId.State3) - .RegisterState(BasicStateId.State3); - - var result = await machine.RunAsync(BasicStateId.State1); - - Console.WriteLine("\n\nPost Execution Validations:"); - Console.WriteLine("---------------------------"); - - var msgService = services.GetRequiredService(); - Console.WriteLine($"* Message service Counter1: {msgService.Counter1} (expected 9)"); + await DryIocDI.DryIocStateMachine.RunAsync(); - // Ensure all states are registered - var enums = Enum.GetValues().Cast(); - 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(); } } diff --git a/samples/Sample03.DependencyInjection/Sample03.DependencyInjection.csproj b/samples/Sample03.DependencyInjection/Sample03.DependencyInjection.csproj index bc3c792..1efa077 100644 --- a/samples/Sample03.DependencyInjection/Sample03.DependencyInjection.csproj +++ b/samples/Sample03.DependencyInjection/Sample03.DependencyInjection.csproj @@ -7,6 +7,7 @@ + diff --git a/samples/Sample04.ResultTransitions/Program.cs b/samples/Sample04.ResultTransitions/Program.cs new file mode 100644 index 0000000..0cb79dd --- /dev/null +++ b/samples/Sample04.ResultTransitions/Program.cs @@ -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!"); + } +} diff --git a/samples/Sample04.ResultTransitions/Sample04.ResultTransitions.csproj b/samples/Sample04.ResultTransitions/Sample04.ResultTransitions.csproj new file mode 100644 index 0000000..ed9781c --- /dev/null +++ b/samples/Sample04.ResultTransitions/Sample04.ResultTransitions.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/samples/Sample04.ResultTransitions/readme.md b/samples/Sample04.ResultTransitions/readme.md new file mode 100644 index 0000000..ffe8a78 --- /dev/null +++ b/samples/Sample04.ResultTransitions/readme.md @@ -0,0 +1,5 @@ +# Sample 04 - Result Transitions + +_Coming Soon_ + +See, Unit Tests, for an example of how to use Result Transitions for now. diff --git a/samples/Sample05.CommandStates/Program.cs b/samples/Sample05.CommandStates/Program.cs new file mode 100644 index 0000000..fd3bfc6 --- /dev/null +++ b/samples/Sample05.CommandStates/Program.cs @@ -0,0 +1,12 @@ +// Copyright Xeno Innovations, Inc. 2026 +// See the LICENSE file in the project root for more information. + +namespace Sample05.CommandStates; + +internal class Program +{ + private static void Main(string[] args) + { + Console.WriteLine("Coming Soon!"); + } +} diff --git a/samples/Sample05.CommandStates/Sample05.CommandStates.csproj b/samples/Sample05.CommandStates/Sample05.CommandStates.csproj new file mode 100644 index 0000000..ed9781c --- /dev/null +++ b/samples/Sample05.CommandStates/Sample05.CommandStates.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/samples/Sample05.CommandStates/readme.md b/samples/Sample05.CommandStates/readme.md new file mode 100644 index 0000000..936c98f --- /dev/null +++ b/samples/Sample05.CommandStates/readme.md @@ -0,0 +1,5 @@ +# Sample 05 - Command States + +_Coming Soon_ + +See, Unit Tests, for an example of how to use Command States for now. diff --git a/samples/Sample06.PassingEvents/Program.cs b/samples/Sample06.PassingEvents/Program.cs new file mode 100644 index 0000000..d1d7bdd --- /dev/null +++ b/samples/Sample06.PassingEvents/Program.cs @@ -0,0 +1,12 @@ +// Copyright Xeno Innovations, Inc. 2026 +// See the LICENSE file in the project root for more information. + +namespace Sample06.PassingEvents; + +internal class Program +{ + private static void Main(string[] args) + { + Console.WriteLine("Coming Soon!"); + } +} diff --git a/samples/Sample06.PassingEvents/Sample06.PassingEvents.csproj b/samples/Sample06.PassingEvents/Sample06.PassingEvents.csproj new file mode 100644 index 0000000..ed9781c --- /dev/null +++ b/samples/Sample06.PassingEvents/Sample06.PassingEvents.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/samples/Sample06.PassingEvents/readme.md b/samples/Sample06.PassingEvents/readme.md new file mode 100644 index 0000000..2e2811c --- /dev/null +++ b/samples/Sample06.PassingEvents/readme.md @@ -0,0 +1,5 @@ +# Sample 06 - Event Aggregator for Passing Events + +_Coming Soon_ + +See, Unit Tests, for an example of how to use Event Aggregator for Passing Events for now. diff --git a/samples/Sample10.UsingEventIPC/Program.cs b/samples/Sample10.UsingEventIPC/Program.cs new file mode 100644 index 0000000..a0c54c5 --- /dev/null +++ b/samples/Sample10.UsingEventIPC/Program.cs @@ -0,0 +1,12 @@ +// Copyright Xeno Innovations, Inc. 2026 +// See the LICENSE file in the project root for more information. + +namespace Sample10.UsingEventIPC; + +internal class Program +{ + private static void Main(string[] args) + { + Console.WriteLine("Coming Soon!"); + } +} diff --git a/samples/Sample10.UsingEventIPC/Sample10.UsingEventIPC.csproj b/samples/Sample10.UsingEventIPC/Sample10.UsingEventIPC.csproj new file mode 100644 index 0000000..ed9781c --- /dev/null +++ b/samples/Sample10.UsingEventIPC/Sample10.UsingEventIPC.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/samples/Sample10.UsingEventIPC/readme.md b/samples/Sample10.UsingEventIPC/readme.md new file mode 100644 index 0000000..8e36786 --- /dev/null +++ b/samples/Sample10.UsingEventIPC/readme.md @@ -0,0 +1,3 @@ +# Sample 10 - Using Lite.EventIPC for External Event Aggregator + +_Coming Soon_