From 4be3081dc8c0cafa295d4fa7e758b585c6f8ab0d Mon Sep 17 00:00:00 2001 From: ognjenkatic Date: Thu, 8 Oct 2026 13:51:42 +0200 Subject: [PATCH] Register RegisterWaiter only through AddSignalWait (4.4.1) AddConductorSharpPatterns() and AddCSharpLambdaTasks() scan the whole Patterns assembly with MediatR, which also picked up the RegisterWaiter handler. Its ISignalStore dependency is only registered by AddSignalWait, so projects that don't use SignalWait failed DI validation at startup. Both scans now exclude RegisterWaiter; AddSignalWait still registers it. Co-Authored-By: Claude Opus 5.5 --- SKILL.md | 2 + .../ConductorSharp.Client.csproj | 2 +- .../ConductorSharp.Engine.csproj | 2 +- ...ctorSharp.KafkaCancellationNotifier.csproj | 2 +- .../ConductorSharp.Patterns.csproj | 2 +- .../Extensions/ContainerBuilderExtensions.cs | 13 +++- .../ConductorSharp.Toolkit.csproj | 2 +- .../Unit/PatternsRegistrationTests.cs | 63 +++++++++++++++++++ 8 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 test/ConductorSharp.Engine.Tests/Unit/PatternsRegistrationTests.cs diff --git a/SKILL.md b/SKILL.md index 31108d1..b65779d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -456,6 +456,8 @@ services services.RegisterWorkflow(); ``` +`RegisterWaiter` and `ISignalStore` are registered only by `AddSignalWait`. Projects that call `AddConductorSharpPatterns()` or `AddCSharpLambdaTasks()` without `AddSignalWait` need no signal store. + **Important**: The `InMemorySignalStore` is only suitable for development/testing. For production, implement `ISignalStore` with a persistent backend (database, Redis, etc.) to ensure signals survive process restarts and work across multiple instances. #### Using Signal Wait in a Workflow diff --git a/src/ConductorSharp.Client/ConductorSharp.Client.csproj b/src/ConductorSharp.Client/ConductorSharp.Client.csproj index 8e698cc..1a50664 100644 --- a/src/ConductorSharp.Client/ConductorSharp.Client.csproj +++ b/src/ConductorSharp.Client/ConductorSharp.Client.csproj @@ -6,7 +6,7 @@ Codaxy Codaxy ConductorSharp.Client - 4.4.0 + 4.4.1 Client library for Netflix Conductor, with some additional quality of life features. https://github.com/codaxy/conductor-sharp netflix;conductor diff --git a/src/ConductorSharp.Engine/ConductorSharp.Engine.csproj b/src/ConductorSharp.Engine/ConductorSharp.Engine.csproj index d091539..df478ce 100644 --- a/src/ConductorSharp.Engine/ConductorSharp.Engine.csproj +++ b/src/ConductorSharp.Engine/ConductorSharp.Engine.csproj @@ -6,7 +6,7 @@ Codaxy Codaxy ConductorSharp.Engine - 4.4.0 + 4.4.1 Client library for Netflix Conductor, with some additional quality of life features. https://github.com/codaxy/conductor-sharp netflix;conductor diff --git a/src/ConductorSharp.KafkaCancellationNotifier/ConductorSharp.KafkaCancellationNotifier.csproj b/src/ConductorSharp.KafkaCancellationNotifier/ConductorSharp.KafkaCancellationNotifier.csproj index c7033c9..989e052 100644 --- a/src/ConductorSharp.KafkaCancellationNotifier/ConductorSharp.KafkaCancellationNotifier.csproj +++ b/src/ConductorSharp.KafkaCancellationNotifier/ConductorSharp.KafkaCancellationNotifier.csproj @@ -4,7 +4,7 @@ net6.0 enable enable - 4.4.0 + 4.4.1 Codaxy Codaxy diff --git a/src/ConductorSharp.Patterns/ConductorSharp.Patterns.csproj b/src/ConductorSharp.Patterns/ConductorSharp.Patterns.csproj index 7e05ca5..70163e9 100644 --- a/src/ConductorSharp.Patterns/ConductorSharp.Patterns.csproj +++ b/src/ConductorSharp.Patterns/ConductorSharp.Patterns.csproj @@ -7,7 +7,7 @@ False Codaxy Codaxy - 4.4.0 + 4.4.1 diff --git a/src/ConductorSharp.Patterns/Extensions/ContainerBuilderExtensions.cs b/src/ConductorSharp.Patterns/Extensions/ContainerBuilderExtensions.cs index cfd2a94..17d8c26 100644 --- a/src/ConductorSharp.Patterns/Extensions/ContainerBuilderExtensions.cs +++ b/src/ConductorSharp.Patterns/Extensions/ContainerBuilderExtensions.cs @@ -1,3 +1,4 @@ +using System.Reflection; using ConductorSharp.Engine.Extensions; using ConductorSharp.Engine.Interface; using ConductorSharp.Engine.Util; @@ -16,7 +17,7 @@ public static IExecutionManagerBuilder AddConductorSharpPatterns(this IExecution executionManagerBuilder.Builder.RegisterWorkerTask(); executionManagerBuilder.Builder.RegisterWorkerTask(); executionManagerBuilder.Builder.RegisterWorkerTask(); - executionManagerBuilder.Builder.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(typeof(WaitSeconds).Assembly)); + executionManagerBuilder.Builder.AddMediatR(cfg => RegisterPatternHandlers(cfg, typeof(WaitSeconds).Assembly)); return executionManagerBuilder; } @@ -30,7 +31,7 @@ public static IExecutionManagerBuilder AddCSharpLambdaTasks( { options.OwnerEmail = "owneremail@gmail.com"; }); - executionManagerBuilder.Builder.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(typeof(CSharpLambdaTask).Assembly)); + executionManagerBuilder.Builder.AddMediatR(cfg => RegisterPatternHandlers(cfg, typeof(CSharpLambdaTask).Assembly)); executionManagerBuilder.Builder.AddSingleton( new ConfigurationProperty(CSharpLambdaTask.LambdaTaskNameConfigurationProperty, csharpLambdaTaskNamePrefix) ); @@ -95,5 +96,13 @@ public static IExecutionManagerBuilder AddSignalWait( return executionManagerBuilder; } + + // RegisterWaiter depends on ISignalStore, which only AddSignalWait registers, + // so the other registrations leave it out of the assembly scan. + private static void RegisterPatternHandlers(MediatRServiceConfiguration cfg, Assembly assembly) + { + cfg.RegisterServicesFromAssemblies(assembly); + cfg.TypeEvaluator = type => type != typeof(RegisterWaiter); + } } } diff --git a/src/ConductorSharp.Toolkit/ConductorSharp.Toolkit.csproj b/src/ConductorSharp.Toolkit/ConductorSharp.Toolkit.csproj index 1359741..0b48430 100644 --- a/src/ConductorSharp.Toolkit/ConductorSharp.Toolkit.csproj +++ b/src/ConductorSharp.Toolkit/ConductorSharp.Toolkit.csproj @@ -7,7 +7,7 @@ disable true dotnet-conductorsharp - 4.4.0 + 4.4.1 diff --git a/test/ConductorSharp.Engine.Tests/Unit/PatternsRegistrationTests.cs b/test/ConductorSharp.Engine.Tests/Unit/PatternsRegistrationTests.cs new file mode 100644 index 0000000..5998243 --- /dev/null +++ b/test/ConductorSharp.Engine.Tests/Unit/PatternsRegistrationTests.cs @@ -0,0 +1,63 @@ +using ConductorSharp.Engine.Extensions; +using ConductorSharp.Engine.Interface; +using ConductorSharp.Patterns.Extensions; +using ConductorSharp.Patterns.Services; +using ConductorSharp.Patterns.Tasks; +using MediatR; +using Microsoft.Extensions.DependencyInjection; + +namespace ConductorSharp.Engine.Tests.Unit +{ + public class PatternsRegistrationTests + { + private static readonly ServiceProviderOptions ValidateOnBuild = new() { ValidateOnBuild = true, ValidateScopes = true }; + + private static (IServiceCollection Services, IExecutionManagerBuilder Builder) CreateBuilder() + { + var services = new ServiceCollection(); + var builder = services + .AddConductorSharp(baseUrl: "http://empty/empty") + .AddExecutionManager( + maxConcurrentWorkers: 1, + sleepInterval: 1, + longPollInterval: 1, + domain: null, + handlerAssemblies: typeof(PatternsRegistrationTests).Assembly + ); + + return (services, builder); + } + + [Fact] + public void PatternsWithoutSignalWaitDoNotRequireSignalStore() + { + var (services, builder) = CreateBuilder(); + builder.AddConductorSharpPatterns().AddCSharpLambdaTasks(); + + using var provider = services.BuildServiceProvider(ValidateOnBuild); + + Assert.DoesNotContain(services, d => d.ImplementationType == typeof(RegisterWaiter)); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void SignalWaitRegistersRegisterWaiterHandler(bool signalWaitFirst) + { + var (services, builder) = CreateBuilder(); + if (signalWaitFirst) + builder.AddSignalWait().AddConductorSharpPatterns().AddCSharpLambdaTasks(); + else + builder.AddConductorSharpPatterns().AddCSharpLambdaTasks().AddSignalWait(); + + using var provider = services.BuildServiceProvider(ValidateOnBuild); + + Assert.Contains( + services, + d => + d.ServiceType == typeof(IRequestHandler) + && d.ImplementationType == typeof(RegisterWaiter) + ); + } + } +}