From 7d431fd82ed099e6380473c01e70ff6123485ac8 Mon Sep 17 00:00:00 2001 From: Ngoc Son Date: Fri, 29 May 2026 18:06:28 +0700 Subject: [PATCH] Fix stream handler include/exclude filtering --- .../Extensions/ServiceCollectionExtensions.cs | 32 +++++++--- .../AddDispatchRConfigurationTests.cs | 63 +++++++++++++++++++ 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/src/DispatchR/Extensions/ServiceCollectionExtensions.cs b/src/DispatchR/Extensions/ServiceCollectionExtensions.cs index f8c164d..b766c22 100644 --- a/src/DispatchR/Extensions/ServiceCollectionExtensions.cs +++ b/src/DispatchR/Extensions/ServiceCollectionExtensions.cs @@ -53,20 +53,32 @@ public static IServiceCollection AddDispatchR(this IServiceCollection services, var otherHandlerTypes = new HashSet() { pipelineBehaviorType, - streamRequestHandlerType, streamPipelineBehaviorType, syncNotificationHandlerType }; var allTypes = configurationOptions.Assemblies.SelectMany(x => x.GetTypes()).Distinct() - .Where(p => - p.GetInterfaces() is { Length: >= 1 } interfaces && - interfaces - .Where(i => i.IsGenericType) - .Select(i => i.GetGenericTypeDefinition()) - .Any(i => i == requestHandlerType - ? configurationOptions.IsHandlerIncluded(p) - : otherHandlerTypes.Contains(i))) + .Where(type => + { + var genericInterfaces = type.GetInterfaces() + .Where(i => i.IsGenericType) + .Select(i => i.GetGenericTypeDefinition()) + .ToList(); + + var isRequestHandler = genericInterfaces.Contains(requestHandlerType); + var isPipelineBehavior = genericInterfaces.Contains(pipelineBehaviorType); + var isStreamRequestHandler = genericInterfaces.Contains(streamRequestHandlerType); + var isStreamPipelineBehavior = genericInterfaces.Contains(streamPipelineBehaviorType); + + var isConcreteRequestHandler = isRequestHandler && !isPipelineBehavior; + var isConcreteStreamRequestHandler = isStreamRequestHandler && !isStreamPipelineBehavior; + if (isConcreteRequestHandler || isConcreteStreamRequestHandler) + { + return configurationOptions.IsHandlerIncluded(type); + } + + return genericInterfaces.Intersect(otherHandlerTypes).Any(); + }) .ToList(); if (configurationOptions.RegisterNotifications) @@ -79,4 +91,4 @@ public static IServiceCollection AddDispatchR(this IServiceCollection services, return services; } -} \ No newline at end of file +} diff --git a/tests/DispatchR.UnitTest/AddDispatchRConfigurationTests.cs b/tests/DispatchR.UnitTest/AddDispatchRConfigurationTests.cs index 6ca79a6..835526c 100644 --- a/tests/DispatchR.UnitTest/AddDispatchRConfigurationTests.cs +++ b/tests/DispatchR.UnitTest/AddDispatchRConfigurationTests.cs @@ -76,6 +76,48 @@ public void AddDispatchR_ReturnsExpectedResponse_IncludeSingleHandler() Assert.Equal(1, countOfAllSimpleHandlers); } + [Fact] + public void AddDispatchR_DoesNotRegisterStreamHandler_WhenOnlyRequestHandlerIsIncluded() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddDispatchR(cfg => + { + cfg.Assemblies.Add(typeof(Fixture).Assembly); + cfg.RegisterPipelines = true; + cfg.RegisterNotifications = false; + cfg.IncludeHandlers = [Fixture.AnyHandlerRequestWithoutPipeline.GetType()]; + }); + + // Assert + Assert.DoesNotContain(services, p => + p.IsKeyedService && + p.KeyedImplementationType == Fixture.AnyStreamHandler.GetType()); + } + + [Fact] + public void AddDispatchR_ReturnsExpectedResponse_IncludeSingleStreamHandler() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddDispatchR(cfg => + { + cfg.Assemblies.Add(typeof(Fixture).Assembly); + cfg.RegisterPipelines = true; + cfg.RegisterNotifications = false; + cfg.IncludeHandlers = [Fixture.AnyStreamHandler.GetType()]; + }); + + // Assert + Assert.Contains(services, p => + p.IsKeyedService && + p.KeyedImplementationType == Fixture.AnyStreamHandler.GetType()); + } + [Fact] public void AddDispatchR_ReturnsExpectedResponse_ExcludeSingleHandler() { @@ -99,6 +141,27 @@ public void AddDispatchR_ReturnsExpectedResponse_ExcludeSingleHandler() Assert.Equal(0, countOfAllSimpleHandlers); } + [Fact] + public void AddDispatchR_ReturnsExpectedResponse_ExcludeSingleStreamHandler() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddDispatchR(cfg => + { + cfg.Assemblies.Add(typeof(Fixture).Assembly); + cfg.RegisterPipelines = true; + cfg.RegisterNotifications = false; + cfg.ExcludeHandlers = [Fixture.AnyStreamHandler.GetType()]; + }); + + // Assert + Assert.DoesNotContain(services, p => + p.IsKeyedService && + p.KeyedImplementationType == Fixture.AnyStreamHandler.GetType()); + } + [Fact] public void AddDispatchR_ReturnsExpectedResponse_IncludeAndExcludeOneHandlers() {