Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 121 additions & 130 deletions src/DispatchR/Configuration/ServiceRegistrator.cs

Large diffs are not rendered by default.

32 changes: 13 additions & 19 deletions src/DispatchR/Extensions/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,30 +50,24 @@ public static IServiceCollection AddDispatchR(this IServiceCollection services,
var streamPipelineBehaviorType = typeof(IStreamPipelineBehavior<,>);
var syncNotificationHandlerType = typeof(INotificationHandler<>);

var otherHandlerTypes = new HashSet<Type>()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not related to the fix, but I thought we can avoid constructing the handler types array multiple times.

{
pipelineBehaviorType,
streamRequestHandlerType,
streamPipelineBehaviorType,
syncNotificationHandlerType
};

var allTypes = configurationOptions.Assemblies.SelectMany(x => x.GetTypes()).Distinct()
.Where(p =>
{
var interfaces = p.GetInterfaces();
return interfaces.Length >= 1 &&
p.GetInterfaces() is { Length: >= 1 } interfaces &&
interfaces
.Where(i => i.IsGenericType)
.Select(i => i.GetGenericTypeDefinition())
.Any(i =>
{
if (i == requestHandlerType)
{
return configurationOptions.IsHandlerIncluded(p);
}

return new[]
{
pipelineBehaviorType,
streamRequestHandlerType,
streamPipelineBehaviorType,
syncNotificationHandlerType
}.Contains(i);
});
}).ToList();
.Any(i => i == requestHandlerType
? configurationOptions.IsHandlerIncluded(p)
: otherHandlerTypes.Contains(i)))
.ToList();

if (configurationOptions.RegisterNotifications)
{
Expand Down
46 changes: 34 additions & 12 deletions tests/DispatchR.IntegrationTest/NotificationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,31 @@
cfg.RegisterPipelines = false;
cfg.RegisterNotifications = true;
});

var spyPipelineOneMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
var spyPipelineTwoMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
var spyPipelineThreeMock = new Mock<INotificationHandler<MultiHandlersNotification>>();

spyPipelineOneMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
spyPipelineTwoMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
spyPipelineThreeMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));

services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineOneMock.Object);
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineTwoMock.Object);
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineThreeMock.Object);

var serviceProvider = services.BuildServiceProvider();
var mediator = serviceProvider.GetRequiredService<IMediator>();

// Act
await mediator.Publish(new MultiHandlersNotification(Guid.Empty), CancellationToken.None);

// Assert
spyPipelineOneMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
spyPipelineTwoMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
spyPipelineThreeMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
}

[Fact]
public async Task PublishObject_CallsAllHandlers_WhenMultipleHandlersAreRegistered()
{
Expand All @@ -56,29 +56,51 @@
cfg.RegisterPipelines = false;
cfg.RegisterNotifications = true;
});

var spyPipelineOneMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
var spyPipelineTwoMock = new Mock<INotificationHandler<MultiHandlersNotification>>();
var spyPipelineThreeMock = new Mock<INotificationHandler<MultiHandlersNotification>>();

spyPipelineOneMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
spyPipelineTwoMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));
spyPipelineThreeMock.Setup(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()));

services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineOneMock.Object);
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineTwoMock.Object);
services.AddScoped<INotificationHandler<MultiHandlersNotification>>(sp => spyPipelineThreeMock.Object);

var serviceProvider = services.BuildServiceProvider();
var mediator = serviceProvider.GetRequiredService<IMediator>();

// Act
object notificationObject = new MultiHandlersNotification(Guid.Empty);
await mediator.Publish(notificationObject, CancellationToken.None);

Check warning on line 77 in tests/DispatchR.IntegrationTest/NotificationTests.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

'IMediator.Publish(object, CancellationToken)' is obsolete: 'This method has performance issues. Use only if strictly necessary'

// Assert
spyPipelineOneMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
spyPipelineTwoMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
spyPipelineThreeMock.Verify(p => p.Handle(It.IsAny<MultiHandlersNotification>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
}
}

[Fact]
public void RegisterNotification_SingleClassWithMultipleNotificationInterfaces_ResolvesAllHandlers()
{
// Arrange
var services = new ServiceCollection();
services.AddDispatchR(cfg =>
{
cfg.Assemblies.Add(typeof(Fixture).Assembly);
cfg.RegisterNotifications = true;
});

var serviceProvider = services.BuildServiceProvider();

// Act
var handlers1 = serviceProvider.GetServices<INotificationHandler<MultiHandlersNotification>>();
var handlers2 = serviceProvider.GetServices<INotificationHandler<MultiHandlersNotification2>>();

// Assert
Assert.Contains(handlers1, h => h is MultiNotificationHandler);
Assert.Contains(handlers2, h => h is MultiNotificationHandler);
}
}
24 changes: 24 additions & 0 deletions tests/DispatchR.IntegrationTest/RequestHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using DispatchR.Abstractions.Send;
using DispatchR.Extensions;
using DispatchR.TestCommon.Fixtures;
using DispatchR.TestCommon.Fixtures.SendRequest;
using DispatchR.TestCommon.Fixtures.SendRequest.Task;
using DispatchR.TestCommon.Fixtures.SendRequest.ValueTask;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
Expand Down Expand Up @@ -54,4 +56,26 @@ public async Task Send_UsesPipelineBehaviors_RequestWithSinglePipelines()
spyPipelineOneMock.Verify(p => p.Handle(It.IsAny<PingValueTask>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
spyPipelineTwoMock.Verify(p => p.Handle(It.IsAny<PingValueTask>(), It.IsAny<CancellationToken>()), Times.Exactly(1));
}

[Fact]
public void RegisterHandlers_SingleClassWithMultipleRequestInterfaces_ResolvesAllHandlers()
{
// Arrange
var services = new ServiceCollection();
services.AddDispatchR(cfg =>
{
cfg.Assemblies.Add(typeof(Fixture).Assembly);
cfg.RegisterPipelines = false;
});

var serviceProvider = services.BuildServiceProvider();

// Act
var handlers1 = serviceProvider.GetServices<IRequestHandler<PingTask, Task<int>>>();
var handlers2 = serviceProvider.GetServices<IRequestHandler<PingValueTask, ValueTask<int>>>();

// Assert
Assert.Contains(handlers1, h => h is MultiRequestHandler);
Assert.Contains(handlers2, h => h is MultiRequestHandler);
}
}
4 changes: 4 additions & 0 deletions tests/DispatchR.TestCommon/DispatchR.TestCommon.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\DispatchR\DispatchR.csproj" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using DispatchR.Abstractions.Notification;

namespace DispatchR.TestCommon.Fixtures.Notification;

public sealed record MultiHandlersNotification2(Guid Id) : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using DispatchR.Abstractions.Notification;

namespace DispatchR.TestCommon.Fixtures.Notification;

public sealed class MultiNotificationHandler :
INotificationHandler<MultiHandlersNotification>,
INotificationHandler<MultiHandlersNotification2>
{
public ValueTask Handle(MultiHandlersNotification request, CancellationToken cancellationToken)
{
return ValueTask.CompletedTask;
}

public ValueTask Handle(MultiHandlersNotification2 request, CancellationToken cancellationToken)
{
return ValueTask.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using DispatchR.Abstractions.Send;
using DispatchR.TestCommon.Fixtures.SendRequest.Task;
using DispatchR.TestCommon.Fixtures.SendRequest.ValueTask;

namespace DispatchR.TestCommon.Fixtures.SendRequest;

public sealed class MultiRequestHandler :
IRequestHandler<PingTask, Task<int>>,
IRequestHandler<PingValueTask, ValueTask<int>>
{
public Task<int> Handle(PingTask request, CancellationToken cancellationToken)
{
return System.Threading.Tasks.Task.FromResult(1);
}

public ValueTask<int> Handle(PingValueTask request, CancellationToken cancellationToken)
{
return System.Threading.Tasks.ValueTask.FromResult(1);
}
}
Loading