Skip to content

Commit ed65b23

Browse files
authored
Support registering multiple handlers in the same class (#41)
* Support registering multiple handlers in the same class * Exclude TestCommon from code coverage
1 parent 22b7d02 commit ed65b23

File tree

8 files changed

+239
-161
lines changed

8 files changed

+239
-161
lines changed

src/DispatchR/Configuration/ServiceRegistrator.cs

Lines changed: 121 additions & 130 deletions
Large diffs are not rendered by default.

src/DispatchR/Extensions/ServiceCollectionExtensions.cs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,30 +50,24 @@ public static IServiceCollection AddDispatchR(this IServiceCollection services,
5050
var streamPipelineBehaviorType = typeof(IStreamPipelineBehavior<,>);
5151
var syncNotificationHandlerType = typeof(INotificationHandler<>);
5252

53+
var otherHandlerTypes = new HashSet<Type>()
54+
{
55+
pipelineBehaviorType,
56+
streamRequestHandlerType,
57+
streamPipelineBehaviorType,
58+
syncNotificationHandlerType
59+
};
60+
5361
var allTypes = configurationOptions.Assemblies.SelectMany(x => x.GetTypes()).Distinct()
5462
.Where(p =>
55-
{
56-
var interfaces = p.GetInterfaces();
57-
return interfaces.Length >= 1 &&
63+
p.GetInterfaces() is { Length: >= 1 } interfaces &&
5864
interfaces
5965
.Where(i => i.IsGenericType)
6066
.Select(i => i.GetGenericTypeDefinition())
61-
.Any(i =>
62-
{
63-
if (i == requestHandlerType)
64-
{
65-
return configurationOptions.IsHandlerIncluded(p);
66-
}
67-
68-
return new[]
69-
{
70-
pipelineBehaviorType,
71-
streamRequestHandlerType,
72-
streamPipelineBehaviorType,
73-
syncNotificationHandlerType
74-
}.Contains(i);
75-
});
76-
}).ToList();
67+
.Any(i => i == requestHandlerType
68+
? configurationOptions.IsHandlerIncluded(p)
69+
: otherHandlerTypes.Contains(i)))
70+
.ToList();
7771

7872
if (configurationOptions.RegisterNotifications)
7973
{

tests/DispatchR.IntegrationTest/NotificationTests.cs

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,31 @@ public async Task Publish_CallsAllHandlers_WhenMultipleHandlersAreRegistered()
2020
cfg.RegisterPipelines = false;
2121
cfg.RegisterNotifications = true;
2222
});
23-
23+
2424
var spyPipelineOneMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
2525
var spyPipelineTwoMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
2626
var spyPipelineThreeMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
2727

2828
spyPipelineOneMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
2929
spyPipelineTwoMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
3030
spyPipelineThreeMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
31-
31+
3232
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineOneMock.Object);
3333
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineTwoMock.Object);
3434
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineThreeMock.Object);
35-
35+
3636
var serviceProvider = services.BuildServiceProvider();
3737
var mediator = serviceProvider.GetRequiredService<IMediator>();
38-
38+
3939
// Act
4040
await mediator.Publish(new MultiHandlersNotification(Guid.Empty), CancellationToken.None);
41-
41+
4242
// Assert
4343
spyPipelineOneMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
4444
spyPipelineTwoMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
4545
spyPipelineThreeMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
4646
}
47-
47+
4848
[Fact]
4949
public async Task PublishObject_CallsAllHandlers_WhenMultipleHandlersAreRegistered()
5050
{
@@ -56,29 +56,51 @@ public async Task PublishObject_CallsAllHandlers_WhenMultipleHandlersAreRegister
5656
cfg.RegisterPipelines = false;
5757
cfg.RegisterNotifications = true;
5858
});
59-
59+
6060
var spyPipelineOneMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
6161
var spyPipelineTwoMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
6262
var spyPipelineThreeMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
6363

6464
spyPipelineOneMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
6565
spyPipelineTwoMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
6666
spyPipelineThreeMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
67-
67+
6868
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineOneMock.Object);
6969
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineTwoMock.Object);
7070
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineThreeMock.Object);
71-
71+
7272
var serviceProvider = services.BuildServiceProvider();
7373
var mediator = serviceProvider.GetRequiredService<IMediator>();
74-
74+
7575
// Act
7676
object notificationObject = new MultiHandlersNotification(Guid.Empty);
7777
await mediator.Publish(notificationObject, CancellationToken.None);
78-
78+
7979
// Assert
8080
spyPipelineOneMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
8181
spyPipelineTwoMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
8282
spyPipelineThreeMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
8383
}
84-
}
84+
85+
[Fact]
86+
public void RegisterNotification_SingleClassWithMultipleNotificationInterfaces_ResolvesAllHandlers()
87+
{
88+
// Arrange
89+
var services = new ServiceCollection();
90+
services.AddDispatchR(cfg =>
91+
{
92+
cfg.Assemblies.Add(typeof(Fixture).Assembly);
93+
cfg.RegisterNotifications = true;
94+
});
95+
96+
var serviceProvider = services.BuildServiceProvider();
97+
98+
// Act
99+
var handlers1 = serviceProvider.GetServices<INotificationHandler<MultiHandlersNotification>>();
100+
var handlers2 = serviceProvider.GetServices<INotificationHandler<MultiHandlersNotification2>>();
101+
102+
// Assert
103+
Assert.Contains(handlers1, h => h is MultiNotificationHandler);
104+
Assert.Contains(handlers2, h => h is MultiNotificationHandler);
105+
}
106+
}

tests/DispatchR.IntegrationTest/RequestHandlerTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using DispatchR.Abstractions.Send;
22
using DispatchR.Extensions;
33
using DispatchR.TestCommon.Fixtures;
4+
using DispatchR.TestCommon.Fixtures.SendRequest;
5+
using DispatchR.TestCommon.Fixtures.SendRequest.Task;
46
using DispatchR.TestCommon.Fixtures.SendRequest.ValueTask;
57
using Microsoft.Extensions.DependencyInjection;
68
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -54,4 +56,26 @@ public async Task Send_UsesPipelineBehaviors_RequestWithSinglePipelines()
5456
spyPipelineOneMock.Verify(p => p.Handle(It.IsAny<PingValueTask>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
5557
spyPipelineTwoMock.Verify(p => p.Handle(It.IsAny<PingValueTask>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
5658
}
59+
60+
[Fact]
61+
public void RegisterHandlers_SingleClassWithMultipleRequestInterfaces_ResolvesAllHandlers()
62+
{
63+
// Arrange
64+
var services = new ServiceCollection();
65+
services.AddDispatchR(cfg =>
66+
{
67+
cfg.Assemblies.Add(typeof(Fixture).Assembly);
68+
cfg.RegisterPipelines = false;
69+
});
70+
71+
var serviceProvider = services.BuildServiceProvider();
72+
73+
// Act
74+
var handlers1 = serviceProvider.GetServices<IRequestHandler<PingTask, Task<int>>>();
75+
var handlers2 = serviceProvider.GetServices<IRequestHandler<PingValueTask, ValueTask<int>>>();
76+
77+
// Assert
78+
Assert.Contains(handlers1, h => h is MultiRequestHandler);
79+
Assert.Contains(handlers2, h => h is MultiRequestHandler);
80+
}
5781
}

tests/DispatchR.TestCommon/DispatchR.TestCommon.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<AssemblyAttribute Include="System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute" />
11+
</ItemGroup>
812

913
<ItemGroup>
1014
<ProjectReference Include="..\..\src\DispatchR\DispatchR.csproj" />
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
using DispatchR.Abstractions.Notification;
2+
3+
namespace DispatchR.TestCommon.Fixtures.Notification;
4+
5+
public sealed record MultiHandlersNotification2(Guid Id) : INotification;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using DispatchR.Abstractions.Notification;
2+
3+
namespace DispatchR.TestCommon.Fixtures.Notification;
4+
5+
public sealed class MultiNotificationHandler :
6+
INotificationHandler<MultiHandlersNotification>,
7+
INotificationHandler<MultiHandlersNotification2>
8+
{
9+
public ValueTask Handle(MultiHandlersNotification request, CancellationToken cancellationToken)
10+
{
11+
return ValueTask.CompletedTask;
12+
}
13+
14+
public ValueTask Handle(MultiHandlersNotification2 request, CancellationToken cancellationToken)
15+
{
16+
return ValueTask.CompletedTask;
17+
}
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using DispatchR.Abstractions.Send;
2+
using DispatchR.TestCommon.Fixtures.SendRequest.Task;
3+
using DispatchR.TestCommon.Fixtures.SendRequest.ValueTask;
4+
5+
namespace DispatchR.TestCommon.Fixtures.SendRequest;
6+
7+
public sealed class MultiRequestHandler :
8+
IRequestHandler<PingTask, Task<int>>,
9+
IRequestHandler<PingValueTask, ValueTask<int>>
10+
{
11+
public Task<int> Handle(PingTask request, CancellationToken cancellationToken)
12+
{
13+
return System.Threading.Tasks.Task.FromResult(1);
14+
}
15+
16+
public ValueTask<int> Handle(PingValueTask request, CancellationToken cancellationToken)
17+
{
18+
return System.Threading.Tasks.ValueTask.FromResult(1);
19+
}
20+
}

0 commit comments

Comments
 (0)