From dfe7c7e7b7885126c918fbee32f24739315e0ab9 Mon Sep 17 00:00:00 2001 From: "Kasper B. Graversen" Date: Tue, 27 Feb 2024 18:28:55 +0100 Subject: [PATCH 1/5] ioc interface fixed and introduce short-cut extension method for autofac --- README.md | 2 +- .../MicroWorkflow.ConsoleDemo.csproj | 6 ++ src/Demos/ConsoleDemo/Program.cs | 26 +++---- src/Demos/MicroWorkflow.Tests/TestHelper.cs | 6 +- src/Demos/WebApiDemo/RegisterGreenFeetWF.cs | 2 +- .../AutofacAdaptor.cs | 77 ++++++++++++++++--- .../AutofacHelper.cs | 35 --------- .../DemoInMemoryPersister.cs | 4 +- .../DemoImplementations/DemoIocContainer.cs | 53 ------------- src/Product/MicroWorkflow/Interfaces.cs | 16 +++- src/Product/MicroWorkflow/ReflectionHelper.cs | 18 +++++ src/Product/MicroWorkflow/Worker.cs | 2 +- src/Product/MicroWorkflow/todos.md | 5 -- 13 files changed, 119 insertions(+), 133 deletions(-) delete mode 100644 src/Product/MicroWorkflow.Ioc.Autofac/AutofacHelper.cs delete mode 100644 src/Product/MicroWorkflow/DemoImplementations/DemoIocContainer.cs diff --git a/README.md b/README.md index ff9fb4f..a0f328b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MicroWorkflow .net +# Micro Workflow .net [![Stats](https://img.shields.io/badge/Code_lines-1,7_K-ff69b4.svg)]() [![Stats](https://img.shields.io/badge/Test_lines-1,2_K-69ffb4.svg)]() diff --git a/src/Demos/ConsoleDemo/MicroWorkflow.ConsoleDemo.csproj b/src/Demos/ConsoleDemo/MicroWorkflow.ConsoleDemo.csproj index 4da8658..e461959 100644 --- a/src/Demos/ConsoleDemo/MicroWorkflow.ConsoleDemo.csproj +++ b/src/Demos/ConsoleDemo/MicroWorkflow.ConsoleDemo.csproj @@ -10,6 +10,12 @@ + + + + + + diff --git a/src/Demos/ConsoleDemo/Program.cs b/src/Demos/ConsoleDemo/Program.cs index 02349f3..48c8e09 100644 --- a/src/Demos/ConsoleDemo/Program.cs +++ b/src/Demos/ConsoleDemo/Program.cs @@ -1,4 +1,5 @@ -using MicroWorkflow; +using Autofac; +using MicroWorkflow; using MicroWorkflow.DemoImplementation; using System.Reflection; using System.Text.Json; @@ -56,8 +57,7 @@ public async Task ExecuteAsync(Step step) } [StepName(Name)] -[StepName("v2/alternative-name")] // step implementation may have multiple names -class SendEmail : IStepImplementation +class SendEmail(EmailSender sender) : IStepImplementation { public const string Name = "v1/demos/fetch-wordanalyzeemail/ship-results"; @@ -66,8 +66,7 @@ public async Task ExecuteAsync(Step step) var topWords = JsonSerializer.Deserialize(step.State!); var words = string.Join(", ", topWords!); - await new EmailSender() - .SendEmail(to: "demos@demoland.com", from: "some@one.cool", $"Top 3 words: {words}"); + await sender.SendEmail(to: "demos@demoland.com", from: "some@one.cool", $"Top 3 words: {words}"); return step.Done(); } @@ -77,14 +76,6 @@ class Program { public static void Main() { - // register the steps by scanning the assembly - var iocContainer = new DemoIocContainer().RegisterNamedSteps(Assembly.GetExecutingAssembly()); - // we persist in-memory - iocContainer.RegisterInstance(typeof(IStepPersister), new DemoInMemoryPersister()); - - // For the demo we tell the engine to stop when there is no immediate pending work, so the program terminates quickly. For production you want the engine to run forever - // The number of workers is dynamically adjusted during execution to fit the pending work. - // This ensures we do not constantly bombard the persistence storage with requests while at the same time quickly respond to new work var cfg = new WorkflowConfiguration( new WorkerConfig { @@ -93,12 +84,13 @@ public static void Main() MaxWorkerCount = 8, }); - // register the logger. Loglevels can change at run-time so you can turn on e.g. fine-grained logs for a limited time - var logger = new ConsoleStepLogger(cfg.LoggerConfiguration); - - var engine = new WorkflowEngine(logger, iocContainer, new DotNetStepStateFormatterJson(logger)); + var builder = new ContainerBuilder(); + builder.UseMicroWorkflow(cfg); + builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsSelf(); + var container = builder.Build(); // Add a step to be executed - you can add new steps at any time during run-time + var engine = container.Resolve(); engine.Data.AddStep(new Step(FetchData.Name, 0)); // Start the engine and wait for it to terminate diff --git a/src/Demos/MicroWorkflow.Tests/TestHelper.cs b/src/Demos/MicroWorkflow.Tests/TestHelper.cs index fa968c3..d9323c8 100644 --- a/src/Demos/MicroWorkflow.Tests/TestHelper.cs +++ b/src/Demos/MicroWorkflow.Tests/TestHelper.cs @@ -15,7 +15,7 @@ public class TestHelper public readonly string FlowId = guid(); public IWorkflowLogger? Logger; public WorkflowEngine? Engine; - public (string, IStepImplementation)[] StepHandlers { get; set; } = Array.Empty<(string, IStepImplementation)>(); + public (string name, IStepImplementation implementation)[] StepHandlers { get; set; } = Array.Empty<(string, IStepImplementation)>(); readonly ContainerBuilder builder = new(); public string ConnectionString = "Server=localhost;Database=adotest;Integrated Security=True;TrustServerCertificate=True"; @@ -50,11 +50,11 @@ public WorkflowEngine Build() ((DiagnosticsStepLogger)Logger).AddNestedLogger(new ConsoleStepLogger(WorkflowConfiguration.LoggerConfiguration)); } - builder.RegisterInstances(Logger, StepHandlers); + builder.RegisterWorkflowSteps(StepHandlers.ToArray()); builder.Register(c => new SqlServerPersister(ConnectionString, Logger)).InstancePerDependency(); // register all classes having a [step] attribute - builder.RegisterStepImplementations(Logger, typeof(TestHelper).Assembly); + builder.RegisterWorkflowSteps(typeof(TestHelper).Assembly); Formatter ??= new NewtonsoftStateFormatterJson(Logger); diff --git a/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs b/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs index d63b489..4770b88 100644 --- a/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs +++ b/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs @@ -23,6 +23,6 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As(); // find and register all step-implementations - builder.RegisterStepImplementations(null, GetType().Assembly); + builder.RegisterWorkflowSteps(GetType().Assembly); } } \ No newline at end of file diff --git a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs index 6240438..78035a0 100644 --- a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs +++ b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs @@ -1,29 +1,82 @@ -using Autofac; +using System.Reflection; +using Autofac; namespace MicroWorkflow; public class AutofacAdaptor : IWorkflowIocContainer { - readonly IComponentContext container; + readonly IComponentContext? container; + readonly ContainerBuilder? builder; - public AutofacAdaptor(IComponentContext container) + public AutofacAdaptor(IComponentContext container) => this.container = container ?? throw new ArgumentNullException(nameof(container)); + + public AutofacAdaptor(ContainerBuilder builder) => this.builder = builder ?? throw new ArgumentNullException(nameof(builder)); + + public T GetInstance() where T : notnull => container!.Resolve() ?? throw new Exception($"Type {typeof(T)} is not registered"); + + public IStepImplementation? GetStep(string stepName) { - this.container = container; + if (!container!.IsRegisteredWithName(stepName)) + return null; + + return container!.ResolveNamed(stepName); } - public T GetInstance() where T : notnull + public void RegisterWorkflowStep(string stepName, Type implementationType) + => builder!.RegisterType(implementationType).Named(stepName); + + public void RegisterWorkflowStep(string stepName, IStepImplementation instance) + => builder!.RegisterInstance(instance).Named(stepName); +} + +public static class AutofacExtensions +{ + public static void UseMicroWorkflow(this ContainerBuilder builder, WorkflowConfiguration config, params Assembly?[] assemblies) { - var v = container.Resolve() - ?? throw new Exception($"Type {typeof(T)} is not registered"); - return v; + builder.RegisterInstance(config); + builder.RegisterInstance(config.LoggerConfiguration); + + builder.RegisterType().As(); + + builder.RegisterType().AsSelf(); + + assemblies = ReflectionHelper.FindRelevantAssemblies(assemblies); + + var registrar = new AutofacAdaptor(builder); + foreach (var (implementationType, stepName) in ReflectionHelper.GetStepsFromAttribute(assemblies!)) + registrar.RegisterWorkflowStep(stepName, implementationType); + + FindAndRegister(); + FindAndRegister(); + FindAndRegister(); + + void FindAndRegister() where T : notnull + { + var x = GetTypesInherit(assemblies!); + if (x != null) + builder.RegisterType(x).As(); + } } - public IStepImplementation? GetNamedInstance(string statename) + static Type? GetTypesInherit(Assembly[] assembly) + => assembly.SelectMany(x => x.GetTypes()) + .Where(x => x.IsClass && !x.IsAbstract && x.IsAssignableTo(typeof(T))) + .FirstOrDefault(); + + public static void RegisterWorkflowSteps(this ContainerBuilder builder, params Assembly?[] assemblies) { - if (!container.IsRegisteredWithName(statename)) - return null; + if (assemblies == null || assemblies.Length == 0) + assemblies = AppDomain.CurrentDomain.GetAssemblies(); - return container.ResolveNamed(statename); + var registrar = new AutofacAdaptor(builder); + foreach (var (implementationType, stepName) in ReflectionHelper.GetStepsFromAttribute(assemblies!)) + registrar.RegisterWorkflowStep(stepName, implementationType); + } + + public static void RegisterWorkflowSteps(this ContainerBuilder builder, params (string stepName, IStepImplementation implementationType)[] stepHandlers) + { + var registrar = new AutofacAdaptor(builder); + stepHandlers.ToList().ForEach(x => registrar.RegisterWorkflowStep(x.stepName, x.implementationType)); } } diff --git a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacHelper.cs b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacHelper.cs deleted file mode 100644 index 0775039..0000000 --- a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacHelper.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Autofac; -using System.Reflection; - -namespace MicroWorkflow; - -public static class AutofacExtensions -{ - /// for unit testing - builds the autofac from the parameters - public static void RegisterInstances(this ContainerBuilder builder, IWorkflowLogger? logger, params (string, IStepImplementation)[] stepHandlers) - { - stepHandlers.ToList().ForEach(x => - { - if (logger != null && logger.InfoLoggingEnabled) - logger.LogInfo($"Registering step '{x.Item1}' to type '{x.Item2.GetType()}", null, null); - - builder.RegisterInstance(x.Item2).Named(x.Item1); - }); - } - - public static void RegisterStepImplementation(this ContainerBuilder builder, IWorkflowLogger? logger, Type implementationType, string stepName) - { - if (logger != null && logger.InfoLoggingEnabled) - logger.LogInfo($"Registering step '{stepName}' to type '{implementationType}", null, null); - - builder.RegisterType(implementationType).Named(stepName); - } - - - /// Register all implementations that are anotated with the - public static void RegisterStepImplementations(this ContainerBuilder builder, IWorkflowLogger? logger, params Assembly[] assemblies) - { - foreach (var (implementationType, stepName) in ReflectionHelper.GetStepsFromAttribute(assemblies)) - RegisterStepImplementation(builder, logger, implementationType, stepName); - } -} \ No newline at end of file diff --git a/src/Product/MicroWorkflow/DemoImplementations/DemoInMemoryPersister.cs b/src/Product/MicroWorkflow/DemoImplementations/DemoInMemoryPersister.cs index 938d5ef..05d7d00 100644 --- a/src/Product/MicroWorkflow/DemoImplementations/DemoInMemoryPersister.cs +++ b/src/Product/MicroWorkflow/DemoImplementations/DemoInMemoryPersister.cs @@ -6,8 +6,8 @@ /// public class DemoInMemoryPersister : IStepPersister { - readonly object GlobalLock = new(); - int GlobalId = 1; + static readonly object GlobalLock = new(); + static int GlobalId = 1; public static readonly Dictionary ReadySteps = new(); public static readonly Dictionary DoneSteps = new(); diff --git a/src/Product/MicroWorkflow/DemoImplementations/DemoIocContainer.cs b/src/Product/MicroWorkflow/DemoImplementations/DemoIocContainer.cs deleted file mode 100644 index eaee760..0000000 --- a/src/Product/MicroWorkflow/DemoImplementations/DemoIocContainer.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Reflection; - -namespace MicroWorkflow.DemoImplementation; - -/// -/// a demo holder for instances that can be retrieved by the engine -/// -public class DemoIocContainer : IWorkflowIocContainer -{ - public Dictionary Entries = new Dictionary(); - - public DemoIocContainer() - { - } - - public DemoIocContainer(params (string, IStepImplementation)[] stepHandlers) - { - stepHandlers.ToList().ForEach(x => { Entries.Add(x.Item1, x.Item2); }); - } - - public T GetInstance() - { - return (T)Entries.First(x => x.Key == typeof(T).FullName).Value; - } - - public IStepImplementation? GetNamedInstance(string stepName) - { - if (Entries.TryGetValue(stepName, out var x)) - return (IStepImplementation)x; - - return null; - } - - public DemoIocContainer RegisterInstance(Type t, object x) - { - Entries.Add(t.FullName!, x); - return this; - } - - public DemoIocContainer RegisterNamedSteps(Assembly assembly) - { - (Type implementationType, string stepName)[] registrations = ReflectionHelper.GetStepsFromAttribute(assembly).ToArray(); - - foreach (var registration in registrations) - { - var instance = Activator.CreateInstance(registration.implementationType); - Entries.Add(registration.stepName, instance!); - } - - return this; - } -} - diff --git a/src/Product/MicroWorkflow/Interfaces.cs b/src/Product/MicroWorkflow/Interfaces.cs index ea94f8e..843ec64 100644 --- a/src/Product/MicroWorkflow/Interfaces.cs +++ b/src/Product/MicroWorkflow/Interfaces.cs @@ -28,9 +28,19 @@ public interface IWorkflowLogger public interface IWorkflowIocContainer { /// implement to return null wnen registration is not found, and throw exception on creation failure. - IStepImplementation? GetNamedInstance(string stepName); + IStepImplementation? GetStep(string stepName); + /// implement to return null wnen registration is not found, and throw exception on creation failure. T GetInstance() where T : notnull; + + void RegisterWorkflowStep(string stepName, Type implementationType); + + void RegisterWorkflowSteps((string stepName, Type implementationType)[] stepHandlers) + => stepHandlers.ToList().ForEach(x => RegisterWorkflowStep(x.stepName, x.implementationType)); + void RegisterWorkflowStep(string stepName, IStepImplementation instance); + + void RegisterWorkflowSteps((string stepName, IStepImplementation instance)[] stepHandlers) + => stepHandlers.ToList().ForEach(x => RegisterWorkflowStep(x.stepName, x.instance)); } /// @@ -39,7 +49,7 @@ public interface IWorkflowIocContainer public interface IStepPersister : IDisposable { string GetConnectionInfoForLogging(); - + T InTransaction(Func code, object? transaction = null); object CreateTransaction(); /// You can either set the transaction explicitly or create one using @@ -52,7 +62,7 @@ public interface IStepPersister : IDisposable public List SearchSteps(SearchModel criteria, StepStatus target); Dictionary> SearchSteps(SearchModel criteria, FetchLevels fetchLevels); - + Dictionary CountTables(string? flowId = null); int Delete(StepStatus target, int id); int Insert(StepStatus target, Step step); diff --git a/src/Product/MicroWorkflow/ReflectionHelper.cs b/src/Product/MicroWorkflow/ReflectionHelper.cs index 63603c6..7f17cb5 100644 --- a/src/Product/MicroWorkflow/ReflectionHelper.cs +++ b/src/Product/MicroWorkflow/ReflectionHelper.cs @@ -28,4 +28,22 @@ public class ReflectionHelper .SelectMany(x => x.attrs, resultSelector: (x, a) => (x.type, a.Name)); return x; } + + public static Assembly[] FindRelevantAssemblies(Assembly?[] assemblies) + { + if (assemblies != null && assemblies.Length > 0) + return assemblies!; + + var ass = AppDomain.CurrentDomain.GetAssemblies() + .Where(x => x.FullName != null && !x.FullName.StartsWith("System") && !x.FullName.StartsWith("Microsoft")) + .ToArray(); + + Assembly[] allAssembliesWithMicroWorkflowAsLast = new[] { Assembly.GetEntryAssembly() } + .Concat(ass.Where(x => !x.FullName.StartsWith("MicroWorkflow"))) + .Concat(ass.Where(x => x.FullName.StartsWith("MicroWorkflow"))) + .Distinct() + .ToArray()!; + + return allAssembliesWithMicroWorkflowAsLast; + } } diff --git a/src/Product/MicroWorkflow/Worker.cs b/src/Product/MicroWorkflow/Worker.cs index 6fc961e..63e9a93 100644 --- a/src/Product/MicroWorkflow/Worker.cs +++ b/src/Product/MicroWorkflow/Worker.cs @@ -234,7 +234,7 @@ async Task FetchExecuteStoreStep() return status!.Value; } - IStepImplementation? implementation = iocContainer.GetNamedInstance(step.Name); + IStepImplementation? implementation = iocContainer.GetStep(step.Name); if (implementation == null) { LogAndRescheduleStep(persister, step); diff --git a/src/Product/MicroWorkflow/todos.md b/src/Product/MicroWorkflow/todos.md index cdbdcf5..b835bc1 100644 --- a/src/Product/MicroWorkflow/todos.md +++ b/src/Product/MicroWorkflow/todos.md @@ -7,10 +7,6 @@ * done ready task - spawn new task to ensure we perform the operaton in case the tast is rerunning and is long to execute - worst case a direct call would time out waiting * fail ready task - spawn new task to ensure we perform the operaton in case the tast is rerunning and is long to execute - worst case a direct call would time out waiting * activateWaitingReadyTask -* AddStepIfNotExist - * method that first check the db - then inserts.. - * since there can be multiple instances of the engine running, we can still have a race condition, we still need to catch an duplicatekey exception that we need to create and use in the persistence layer. - * SearchModel needs be extended ### documentation * Getting started guide @@ -29,4 +25,3 @@ ### Performance todo's * Add steps to insert using prepared sql and possibly multiple values * Hardcode column over GetOrdinal() for added speed -* MAYBE delay should have a counter in each worker, if some threshold has been reached wait for a longer period - current implementation block performane of e.g few repeating jobs \ No newline at end of file From b94dcf925ed55b3de12062b97b9f27fe4ce7316e Mon Sep 17 00:00:00 2001 From: "Kasper B. Graversen" Date: Tue, 27 Feb 2024 18:29:13 +0100 Subject: [PATCH 2/5] rename IStepPersister to IWorkflowStepPersister --- src/Demos/MicroWorkflow.Tests/TestHelper.cs | 4 ++-- src/Demos/WebApiDemo/RegisterGreenFeetWF.cs | 2 +- .../MicroWorkflow.AdoPersistence/AdoDb.cs | 2 +- .../MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs | 2 +- .../DemoImplementations/DemoInMemoryPersister.cs | 2 +- src/Product/MicroWorkflow/Interfaces.cs | 2 +- src/Product/MicroWorkflow/Worker.cs | 14 ++++++++------ src/Product/MicroWorkflow/WorkflowEngine.cs | 2 +- src/Product/MicroWorkflow/WorkflowRuntimeData.cs | 16 ++++++++-------- .../MicroWorkflow/WorkflowRuntimeMetrics.cs | 2 +- 10 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/Demos/MicroWorkflow.Tests/TestHelper.cs b/src/Demos/MicroWorkflow.Tests/TestHelper.cs index d9323c8..6cf9d93 100644 --- a/src/Demos/MicroWorkflow.Tests/TestHelper.cs +++ b/src/Demos/MicroWorkflow.Tests/TestHelper.cs @@ -10,7 +10,7 @@ public class TestHelper public NewtonsoftStateFormatterJson? Formatter; public CancellationTokenSource cts = new(); public AutofacAdaptor? iocContainer; - public SqlServerPersister Persister => (SqlServerPersister)iocContainer!.GetInstance(); + public SqlServerPersister Persister => (SqlServerPersister)iocContainer!.GetInstance(); public readonly string CorrelationId = guid(); public readonly string FlowId = guid(); public IWorkflowLogger? Logger; @@ -51,7 +51,7 @@ public WorkflowEngine Build() } builder.RegisterWorkflowSteps(StepHandlers.ToArray()); - builder.Register(c => new SqlServerPersister(ConnectionString, Logger)).InstancePerDependency(); + builder.Register(c => new SqlServerPersister(ConnectionString, Logger)).InstancePerDependency(); // register all classes having a [step] attribute builder.RegisterWorkflowSteps(typeof(TestHelper).Assembly); diff --git a/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs b/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs index 4770b88..756386b 100644 --- a/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs +++ b/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs @@ -8,7 +8,7 @@ public class RegisterWorkflow : Module protected override void Load(ContainerBuilder builder) { // log to in-memory storage - builder.RegisterType().As(); + builder.RegisterType().As(); // use a simple logger builder.RegisterType().As(); diff --git a/src/Product/MicroWorkflow.AdoPersistence/AdoDb.cs b/src/Product/MicroWorkflow.AdoPersistence/AdoDb.cs index af5b0c9..eac0776 100644 --- a/src/Product/MicroWorkflow.AdoPersistence/AdoDb.cs +++ b/src/Product/MicroWorkflow.AdoPersistence/AdoDb.cs @@ -4,7 +4,7 @@ namespace MicroWorkflow; -public class SqlServerPersister : IStepPersister +public class SqlServerPersister : IWorkflowStepPersister { public string TableNameReady { get; set; } = "[dbo].[Steps_Ready]"; public string TableNameFail { get; set; } = "[dbo].[Steps_Fail]"; diff --git a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs index 78035a0..afbaee8 100644 --- a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs +++ b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs @@ -47,7 +47,7 @@ public static void UseMicroWorkflow(this ContainerBuilder builder, WorkflowConfi registrar.RegisterWorkflowStep(stepName, implementationType); FindAndRegister(); - FindAndRegister(); + FindAndRegister(); FindAndRegister(); void FindAndRegister() where T : notnull diff --git a/src/Product/MicroWorkflow/DemoImplementations/DemoInMemoryPersister.cs b/src/Product/MicroWorkflow/DemoImplementations/DemoInMemoryPersister.cs index 05d7d00..ca7bc02 100644 --- a/src/Product/MicroWorkflow/DemoImplementations/DemoInMemoryPersister.cs +++ b/src/Product/MicroWorkflow/DemoImplementations/DemoInMemoryPersister.cs @@ -4,7 +4,7 @@ /// Simple in-memory storage FOR DEMO PURPOSES ONLY. /// The current transaction handling is incorrect! /// -public class DemoInMemoryPersister : IStepPersister +public class DemoInMemoryPersister : IWorkflowStepPersister { static readonly object GlobalLock = new(); static int GlobalId = 1; diff --git a/src/Product/MicroWorkflow/Interfaces.cs b/src/Product/MicroWorkflow/Interfaces.cs index 843ec64..e84f891 100644 --- a/src/Product/MicroWorkflow/Interfaces.cs +++ b/src/Product/MicroWorkflow/Interfaces.cs @@ -46,7 +46,7 @@ void RegisterWorkflowSteps((string stepName, IStepImplementation instance)[] ste /// /// This interface is disposable such that connections/transactions may be cleaned up by the dispose method /// -public interface IStepPersister : IDisposable +public interface IWorkflowStepPersister : IDisposable { string GetConnectionInfoForLogging(); diff --git a/src/Product/MicroWorkflow/Worker.cs b/src/Product/MicroWorkflow/Worker.cs index 63e9a93..bccfeeb 100644 --- a/src/Product/MicroWorkflow/Worker.cs +++ b/src/Product/MicroWorkflow/Worker.cs @@ -71,6 +71,8 @@ enum WorkerRunStatus { Stop, Continue, NoWorkDone, Error } public async Task StartAsync(CancellationToken stoppingToken) { + if (stoppingToken == null) throw new ArgumentNullException(nameof(stoppingToken)); + if (logger.TraceLoggingEnabled) logger.LogTrace($"{nameof(Worker)}: starting worker", null, new Dictionary() { { "workerId", WorkerName } }); @@ -124,7 +126,7 @@ async Task ExecuteLoop() new Dictionary() { { "workerId", WorkerName } }); ResetWaitForWorkers(); - + coordinator.MayWorkerDie(); return; } @@ -179,7 +181,7 @@ void Delay() } } - (Step?, WorkerRunStatus?) GetNextStep(IStepPersister persister) + (Step?, WorkerRunStatus?) GetNextStep(IWorkflowStepPersister persister) { try { @@ -222,7 +224,7 @@ void Delay() async Task FetchExecuteStoreStep() { - using (var persister = iocContainer.GetInstance()) + using (var persister = iocContainer.GetInstance()) { if (CreateTransaction(persister) == null) return WorkerRunStatus.Error; @@ -291,7 +293,7 @@ async Task ExecuteImplementation(IStepImplementation implementa } } - private bool PersistChanges(IStepPersister persister, Step step, ExecutionResult result) + private bool PersistChanges(IWorkflowStepPersister persister, Step step, ExecutionResult result) { try { @@ -328,7 +330,7 @@ private bool PersistChanges(IStepPersister persister, Step step, ExecutionResult return true; } - private void LogAndRescheduleStep(IStepPersister persister, Step step) + private void LogAndRescheduleStep(IWorkflowStepPersister persister, Step step) { var msg = $"{nameof(Worker)}: missing step-implementation for step '{step.Name}'"; if (logger.InfoLoggingEnabled) @@ -340,7 +342,7 @@ private void LogAndRescheduleStep(IStepPersister persister, Step step) persister.Commit(); } - object? CreateTransaction(IStepPersister persister) + object? CreateTransaction(IWorkflowStepPersister persister) { try { diff --git a/src/Product/MicroWorkflow/WorkflowEngine.cs b/src/Product/MicroWorkflow/WorkflowEngine.cs index 4369051..ac9a3af 100644 --- a/src/Product/MicroWorkflow/WorkflowEngine.cs +++ b/src/Product/MicroWorkflow/WorkflowEngine.cs @@ -45,7 +45,7 @@ void Init(WorkflowConfiguration configuration, string? engineName, CancellationT if (logger.InfoLoggingEnabled) { logger.LogInfo($"{nameof(WorkflowEngine)}: starting engine: {engineName}", null, null); - logger.LogInfo($"{nameof(WorkflowEngine)}: starting persister connection: {iocContainer.GetInstance().GetConnectionInfoForLogging()}", null, null); + logger.LogInfo($"{nameof(WorkflowEngine)}: starting persister connection: {iocContainer.GetInstance().GetConnectionInfoForLogging()}", null, null); } Configuration = configuration; diff --git a/src/Product/MicroWorkflow/WorkflowRuntimeData.cs b/src/Product/MicroWorkflow/WorkflowRuntimeData.cs index e7c0338..74965f7 100644 --- a/src/Product/MicroWorkflow/WorkflowRuntimeData.cs +++ b/src/Product/MicroWorkflow/WorkflowRuntimeData.cs @@ -19,7 +19,7 @@ public WorkflowRuntimeData(IWorkflowIocContainer iocContainer, IWorkflowStepStat /// Reschedule a ready step to 'now' and send it activation data public int ActivateStep(int id, object? activationArguments, object? transaction = null) { - var persister = iocContainer.GetInstance(); + var persister = iocContainer.GetInstance(); int rows = persister.InTransaction(() => { @@ -41,7 +41,7 @@ public int ActivateStep(int id, object? activationArguments, object? transaction /// the identity of the step or null if a search found one or more ready steps public int? AddStepIfNotExists(Step step, SearchModel searchModel, object? transaction = null) { - IStepPersister persister = iocContainer.GetInstance(); + IWorkflowStepPersister persister = iocContainer.GetInstance(); transaction ??= persister.CreateTransaction(); @@ -71,7 +71,7 @@ public int[] AddSteps(Step[] steps, object? transaction = null) FixupNewStep(null, step, now); } - IStepPersister persister = iocContainer.GetInstance(); + IWorkflowStepPersister persister = iocContainer.GetInstance(); var result = persister.InTransaction(() => persister.Insert(StepStatus.Ready, steps), transaction); Worker.ResetWaitForWorkers(); @@ -87,7 +87,7 @@ public async Task AddStepsBulkAsync(IEnumerable steps) { var now = DateTime.Now; - var persister = iocContainer.GetInstance(); + var persister = iocContainer.GetInstance(); var fix = steps.Select(x => { @@ -120,14 +120,14 @@ internal void FixupNewStep(Step? originStep, Step step, DateTime now) public List SearchSteps(SearchModel criteria, StepStatus target, object? transaction = null) { - IStepPersister persister = iocContainer.GetInstance(); + IWorkflowStepPersister persister = iocContainer.GetInstance(); var result = persister.InTransaction(() => persister.SearchSteps(criteria, target), transaction); return result; } public Dictionary> SearchSteps(SearchModel criteria, FetchLevels fetchLevels, object? transaction = null) { - IStepPersister persister = iocContainer.GetInstance(); + IWorkflowStepPersister persister = iocContainer.GetInstance(); var result = persister.InTransaction(() => persister.SearchSteps(criteria, fetchLevels), transaction); return result; } @@ -136,7 +136,7 @@ public Dictionary> SearchSteps(SearchModel criteria, Fetc /// Ids of inserted steps into the ready queue public int[] ReExecuteSteps(SearchModel criteria, FetchLevels stepKinds, object? transaction = null) { - IStepPersister persister = iocContainer.GetInstance(); + IWorkflowStepPersister persister = iocContainer.GetInstance(); if (stepKinds.Ready) throw new ArgumentException("Cannot re-execute 'ready' steps"); @@ -187,7 +187,7 @@ public int[] ReExecuteSteps(SearchModel criteria, FetchLevels stepKinds, object? /// The ids of the failed steps public int[] FailSteps(SearchModel criteria, object? transaction = null) { - IStepPersister persister = iocContainer.GetInstance(); + IWorkflowStepPersister persister = iocContainer.GetInstance(); int[] ids = persister.InTransaction(() => { diff --git a/src/Product/MicroWorkflow/WorkflowRuntimeMetrics.cs b/src/Product/MicroWorkflow/WorkflowRuntimeMetrics.cs index f1d2e2d..b840aab 100644 --- a/src/Product/MicroWorkflow/WorkflowRuntimeMetrics.cs +++ b/src/Product/MicroWorkflow/WorkflowRuntimeMetrics.cs @@ -11,7 +11,7 @@ public WorkflowRuntimeMetrics(IWorkflowIocContainer container) public Dictionary CountSteps() { - using var persister = container.GetInstance(); + using var persister = container.GetInstance(); return persister.InTransaction(() => persister.CountTables()); } } From 92ca812e73fb348d2cb94e006b5e9a3243db1395 Mon Sep 17 00:00:00 2001 From: "Kasper B. Graversen" Date: Tue, 27 Feb 2024 18:29:20 +0100 Subject: [PATCH 3/5] rename GetStepsFromAttribute to FindStepsFromAttribute --- .../AutofacAdaptor.cs | 4 ++-- src/Product/MicroWorkflow/ReflectionHelper.cs | 20 +++++++++---------- src/Product/MicroWorkflow/StepStatus.cs | 7 +------ .../MicroWorkflow/WorkerCoordinator.cs | 2 +- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs index afbaee8..f7a91f1 100644 --- a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs +++ b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs @@ -43,7 +43,7 @@ public static void UseMicroWorkflow(this ContainerBuilder builder, WorkflowConfi assemblies = ReflectionHelper.FindRelevantAssemblies(assemblies); var registrar = new AutofacAdaptor(builder); - foreach (var (implementationType, stepName) in ReflectionHelper.GetStepsFromAttribute(assemblies!)) + foreach (var (stepName, implementationType ) in ReflectionHelper.FindStepsFromAttribute(assemblies!)) registrar.RegisterWorkflowStep(stepName, implementationType); FindAndRegister(); @@ -69,7 +69,7 @@ public static void RegisterWorkflowSteps(this ContainerBuilder builder, params A assemblies = AppDomain.CurrentDomain.GetAssemblies(); var registrar = new AutofacAdaptor(builder); - foreach (var (implementationType, stepName) in ReflectionHelper.GetStepsFromAttribute(assemblies!)) + foreach (var (stepName, implementationType) in ReflectionHelper.FindStepsFromAttribute(assemblies!)) registrar.RegisterWorkflowStep(stepName, implementationType); } diff --git a/src/Product/MicroWorkflow/ReflectionHelper.cs b/src/Product/MicroWorkflow/ReflectionHelper.cs index 7f17cb5..25b9d8b 100644 --- a/src/Product/MicroWorkflow/ReflectionHelper.cs +++ b/src/Product/MicroWorkflow/ReflectionHelper.cs @@ -6,9 +6,9 @@ public class ReflectionHelper { /// Harvest all steps annotated with /// When duplicate step names are found - public static IEnumerable<(Type implementationType, string stepName)> GetStepsFromAttribute(params Assembly[] assemblies) + public static IEnumerable<(string stepName, Type implementationType)> FindStepsFromAttribute(params Assembly[] assemblies) { - var result = new Dictionary(); + var result = new Dictionary(); foreach (var step in assemblies.SelectMany(x => GetSteps(x))) { @@ -19,14 +19,14 @@ public class ReflectionHelper } return result.Values; - } - - static IEnumerable<(Type implementationType, string stepName)> GetSteps(Assembly a) - { - var x = a.GetTypes() - .Select(x => new { type = x, attrs = x.GetCustomAttributes() }) - .SelectMany(x => x.attrs, resultSelector: (x, a) => (x.type, a.Name)); - return x; + + static IEnumerable<(string stepName, Type implementationType)> GetSteps(Assembly a) + { + var x = a.GetTypes() + .Select(x => new { type = x, attrs = x.GetCustomAttributes() }) + .SelectMany(x => x.attrs, resultSelector: (x, a) => (a.Name, x.type)); + return x; + } } public static Assembly[] FindRelevantAssemblies(Assembly?[] assemblies) diff --git a/src/Product/MicroWorkflow/StepStatus.cs b/src/Product/MicroWorkflow/StepStatus.cs index bb70fc9..2434dde 100644 --- a/src/Product/MicroWorkflow/StepStatus.cs +++ b/src/Product/MicroWorkflow/StepStatus.cs @@ -1,8 +1,3 @@ namespace MicroWorkflow; -public enum StepStatus -{ - Done, - Failed, - Ready -} \ No newline at end of file +public enum StepStatus { Done, Failed, Ready } \ No newline at end of file diff --git a/src/Product/MicroWorkflow/WorkerCoordinator.cs b/src/Product/MicroWorkflow/WorkerCoordinator.cs index 4f85349..f873e93 100644 --- a/src/Product/MicroWorkflow/WorkerCoordinator.cs +++ b/src/Product/MicroWorkflow/WorkerCoordinator.cs @@ -69,7 +69,7 @@ public bool MayWorkerDie() if (config.StopWhenNoImmediateWork) { if (logger.TraceLoggingEnabled) - logger.LogTrace($"{nameof(WorkerCoordinator)}: TryForceStopEngine WorkerCount:{WorkerCount} MinWorkers:{config.MinWorkerCount}", null, null); + logger.LogTrace($"{nameof(WorkerCoordinator)}: MayWorkerDie WorkerCount:{WorkerCount} MinWorkers:{config.MinWorkerCount}", null, null); if (AreWeAnyOfTheLastRemainingMinWorkers) { From 5136cbec1151cc44cbcc3952533c0362221c8fc9 Mon Sep 17 00:00:00 2001 From: "Kasper B. Graversen" Date: Tue, 27 Feb 2024 18:29:25 +0100 Subject: [PATCH 4/5] fix --- src/Demos/ConsoleDemo/Program.cs | 27 +++++++++---------- .../AutofacAdaptor.cs | 18 ++++++------- src/Product/MicroWorkflow/Interfaces.cs | 2 +- src/Product/MicroWorkflow/ReflectionHelper.cs | 4 +-- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/Demos/ConsoleDemo/Program.cs b/src/Demos/ConsoleDemo/Program.cs index 48c8e09..b5e152f 100644 --- a/src/Demos/ConsoleDemo/Program.cs +++ b/src/Demos/ConsoleDemo/Program.cs @@ -1,7 +1,6 @@ using Autofac; using MicroWorkflow; using MicroWorkflow.DemoImplementation; -using System.Reflection; using System.Text.Json; @@ -44,15 +43,15 @@ public async Task ExecuteAsync(Step step) { var content = JsonSerializer.Deserialize(step.State!); var topWords = content! - .Split(' ', StringSplitOptions.RemoveEmptyEntries) + .Split(' ') .Where(x => x.Length > 3) .GroupBy(x => x) .OrderByDescending(x => x.Count()) .Take(3) .Select(x => x.Key); - return await Task.FromResult( - step.Done().With(new Step(SendEmail.Name, topWords))); + ExecutionResult done = step.Done().With(new Step(SendEmail.Name, topWords)); + return await Task.FromResult(done); } } @@ -76,17 +75,12 @@ class Program { public static void Main() { - var cfg = new WorkflowConfiguration( - new WorkerConfig - { - StopWhenNoImmediateWork = true, - MinWorkerCount = 1, - MaxWorkerCount = 8, - }); - var builder = new ContainerBuilder(); + var cfg = new WorkflowConfiguration(new WorkerConfig { StopWhenNoImmediateWork = true }); builder.UseMicroWorkflow(cfg); - builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsSelf(); + builder.RegisterType().AsSelf(); + builder.RegisterType().As(); + var container = builder.Build(); // Add a step to be executed - you can add new steps at any time during run-time @@ -94,8 +88,13 @@ public static void Main() engine.Data.AddStep(new Step(FetchData.Name, 0)); // Start the engine and wait for it to terminate - engine.Start(cfg); + engine.Start(); + PrintResult(); + } + + private static void PrintResult() + { Console.WriteLine(PrintTable("Ready", DemoInMemoryPersister.ReadySteps)); Console.WriteLine(PrintTable("Failed", DemoInMemoryPersister.FailedSteps)); Console.WriteLine(PrintTable("Done", DemoInMemoryPersister.DoneSteps)); diff --git a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs index f7a91f1..6c4582c 100644 --- a/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs +++ b/src/Product/MicroWorkflow.Ioc.Autofac/AutofacAdaptor.cs @@ -31,19 +31,17 @@ public void RegisterWorkflowStep(string stepName, IStepImplementation instance) public static class AutofacExtensions { - public static void UseMicroWorkflow(this ContainerBuilder builder, WorkflowConfiguration config, params Assembly?[] assemblies) + public static void UseMicroWorkflow(this ContainerBuilder builder, WorkflowConfiguration config, params Assembly?[] assembliesToSearch) { builder.RegisterInstance(config); builder.RegisterInstance(config.LoggerConfiguration); - builder.RegisterType().As(); - builder.RegisterType().AsSelf(); - assemblies = ReflectionHelper.FindRelevantAssemblies(assemblies); + var assemblies = ReflectionHelper.FindRelevantAssemblies(assembliesToSearch ?? new Assembly[0]); var registrar = new AutofacAdaptor(builder); - foreach (var (stepName, implementationType ) in ReflectionHelper.FindStepsFromAttribute(assemblies!)) + foreach (var (stepName, implementationType) in ReflectionHelper.FindStepsFromAttribute(assemblies!)) registrar.RegisterWorkflowStep(stepName, implementationType); FindAndRegister(); @@ -63,9 +61,9 @@ void FindAndRegister() where T : notnull .Where(x => x.IsClass && !x.IsAbstract && x.IsAssignableTo(typeof(T))) .FirstOrDefault(); - public static void RegisterWorkflowSteps(this ContainerBuilder builder, params Assembly?[] assemblies) + public static void RegisterWorkflowSteps(this ContainerBuilder builder, params Assembly[] assemblies) { - if (assemblies == null || assemblies.Length == 0) + if (assemblies.Length == 0) assemblies = AppDomain.CurrentDomain.GetAssemblies(); var registrar = new AutofacAdaptor(builder); @@ -73,10 +71,10 @@ public static void RegisterWorkflowSteps(this ContainerBuilder builder, params A registrar.RegisterWorkflowStep(stepName, implementationType); } - public static void RegisterWorkflowSteps(this ContainerBuilder builder, params (string stepName, IStepImplementation implementationType)[] stepHandlers) + public static void RegisterWorkflowSteps(this ContainerBuilder builder, params (string stepName, IStepImplementation implementation)[] stepHandlers) { var registrar = new AutofacAdaptor(builder); - stepHandlers.ToList().ForEach(x => registrar.RegisterWorkflowStep(x.stepName, x.implementationType)); + foreach(var stepHandler in stepHandlers) + registrar.RegisterWorkflowStep(stepHandler.stepName, stepHandler.implementation); } } - diff --git a/src/Product/MicroWorkflow/Interfaces.cs b/src/Product/MicroWorkflow/Interfaces.cs index e84f891..8a440ff 100644 --- a/src/Product/MicroWorkflow/Interfaces.cs +++ b/src/Product/MicroWorkflow/Interfaces.cs @@ -72,7 +72,7 @@ public interface IWorkflowStepPersister : IDisposable int Update(StepStatus target, Step step); } -public interface IWorkflowStepStateFormatter +public interface IWorkflowStepStateFormatter // TODO rename to IWorkflowStateFormatter { public string StateFormatName { get; } diff --git a/src/Product/MicroWorkflow/ReflectionHelper.cs b/src/Product/MicroWorkflow/ReflectionHelper.cs index 25b9d8b..4078921 100644 --- a/src/Product/MicroWorkflow/ReflectionHelper.cs +++ b/src/Product/MicroWorkflow/ReflectionHelper.cs @@ -29,9 +29,9 @@ public class ReflectionHelper } } - public static Assembly[] FindRelevantAssemblies(Assembly?[] assemblies) + public static Assembly[] FindRelevantAssemblies(Assembly[] assemblies) { - if (assemblies != null && assemblies.Length > 0) + if (assemblies.Length > 0) return assemblies!; var ass = AppDomain.CurrentDomain.GetAssemblies() From eb15a4015a9dd61231ab8f0b66174d4c443e247d Mon Sep 17 00:00:00 2001 From: "Kasper B. Graversen" Date: Tue, 27 Feb 2024 18:29:30 +0100 Subject: [PATCH 5/5] WorkflowConfiguration is now Engine ctor argument --- src/Demos/MicroWorkflow.Tests/TestHelper.cs | 6 +++--- src/Demos/WebApiDemo/RegisterGreenFeetWF.cs | 2 ++ src/Demos/WebApiDemo/WorkflowStarter.cs | 2 +- src/Product/MicroWorkflow/WorkflowEngine.cs | 8 +++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Demos/MicroWorkflow.Tests/TestHelper.cs b/src/Demos/MicroWorkflow.Tests/TestHelper.cs index 6cf9d93..fac1df7 100644 --- a/src/Demos/MicroWorkflow.Tests/TestHelper.cs +++ b/src/Demos/MicroWorkflow.Tests/TestHelper.cs @@ -59,7 +59,7 @@ public WorkflowEngine Build() Formatter ??= new NewtonsoftStateFormatterJson(Logger); iocContainer = new AutofacAdaptor(builder.Build()); - Engine = new WorkflowEngine(Logger, iocContainer, Formatter); + Engine = new WorkflowEngine(WorkflowConfiguration, Logger, iocContainer, Formatter); Engine.Data.AddSteps(Steps); @@ -92,14 +92,14 @@ public WorkflowEngine BuildAndStart() public WorkflowEngine StartAsync() { - Engine!.StartAsync(WorkflowConfiguration, stoppingToken: cts.Token); + Engine!.StartAsync(stoppingToken: cts.Token); return Engine; } public WorkflowEngine Start() { if (Engine == null) throw new Exception("Remember to 'build' before 'start'"); - Engine!.Start(WorkflowConfiguration, stoppingToken: cts.Token); + Engine!.Start(stoppingToken: cts.Token); return Engine; } diff --git a/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs b/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs index 756386b..98819fc 100644 --- a/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs +++ b/src/Demos/WebApiDemo/RegisterGreenFeetWF.cs @@ -24,5 +24,7 @@ protected override void Load(ContainerBuilder builder) // find and register all step-implementations builder.RegisterWorkflowSteps(GetType().Assembly); + + builder.RegisterInstance(new WorkflowConfiguration(new WorkerConfig())); } } \ No newline at end of file diff --git a/src/Demos/WebApiDemo/WorkflowStarter.cs b/src/Demos/WebApiDemo/WorkflowStarter.cs index b60907c..ee5465c 100644 --- a/src/Demos/WebApiDemo/WorkflowStarter.cs +++ b/src/Demos/WebApiDemo/WorkflowStarter.cs @@ -20,7 +20,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) SearchModel searchModel = new(Name: step.Name, Singleton: step.Singleton); engine.Data.AddStepIfNotExists(step, searchModel); - engine.StartAsync(new WorkflowConfiguration(new WorkerConfig()), stoppingToken: stoppingToken); + engine.StartAsync(stoppingToken: stoppingToken); await Task.CompletedTask; } diff --git a/src/Product/MicroWorkflow/WorkflowEngine.cs b/src/Product/MicroWorkflow/WorkflowEngine.cs index ac9a3af..505ab32 100644 --- a/src/Product/MicroWorkflow/WorkflowEngine.cs +++ b/src/Product/MicroWorkflow/WorkflowEngine.cs @@ -7,15 +7,19 @@ public class WorkflowEngine public string? EngineName { get; set; } public CancellationToken StoppingToken { get; private set; } + readonly WorkflowConfiguration configuration; + public WorkerCoordinator? WorkerCoordinator { get; private set; } public WorkflowEngine( + WorkflowConfiguration configuration, IWorkflowLogger logger, IWorkflowIocContainer iocContainer, IWorkflowStepStateFormatter formatter) { this.logger = logger; this.iocContainer = iocContainer; + this.configuration = configuration; Data = new WorkflowRuntimeData(iocContainer, formatter, logger, null); Metrics = new WorkflowRuntimeMetrics(iocContainer); @@ -88,7 +92,6 @@ void Init(WorkflowConfiguration configuration, string? engineName, CancellationT /// start the engine, which starts workers in the background /// public void StartAsync( - WorkflowConfiguration configuration, string? engineName = null, CancellationToken? stoppingToken = null) { @@ -99,11 +102,10 @@ public void StartAsync( /// Start the engine and await the stopping token gets cancelled /// public void Start( - WorkflowConfiguration configuration, string? engineName = null, CancellationToken? stoppingToken = null) { - StartAsync(configuration, engineName, stoppingToken); + StartAsync(engineName, stoppingToken); StoppingToken.WaitHandle.WaitOne(); }