Skip to content

Fix support for multiple notification handlers on 1 class #206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 5, 2025
Merged
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
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ namespace Mediator.SourceGenerator;

internal sealed class NotificationMessageHandler : MessageHandler<NotificationMessageHandler>
{
public readonly INamedTypeSymbol InterfaceSymbol;
public readonly INamedTypeSymbol UnconstructedInterfaceSymbol;
private readonly List<NotificationMessage> _messages;

@@ -18,9 +17,6 @@ CompilationAnalyzer analyzer
: base(symbol, analyzer)
{
UnconstructedInterfaceSymbol = interfaceSymbol;
InterfaceSymbol = symbol.AllInterfaces.Single(i =>
i.IsGenericType && i.OriginalDefinition.Equals(UnconstructedInterfaceSymbol, SymbolEqualityComparer.Default)
);
_messages = new List<NotificationMessage>();
}

Original file line number Diff line number Diff line change
@@ -10,7 +10,9 @@ public NotificationMessageHandlerModel(NotificationMessageHandler handler, Compi
ServiceRegistrations = ImmutableEquatableArray<string>.Empty;
if (handler.Messages.Count > 0)
{
var interfaceSymbol = handler.InterfaceSymbol.GetTypeSymbolFullName(includeTypeParameters: false);
var interfaceSymbol = handler.UnconstructedInterfaceSymbol.GetTypeSymbolFullName(
includeTypeParameters: false
);
var concreteSymbol = handler.Symbol.GetTypeSymbolFullName(includeTypeParameters: false);
var builder = new List<string>(handler.Messages.Count);

83 changes: 83 additions & 0 deletions test/Mediator.SourceGenerator.Tests/BasicTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System.Threading.Tasks;

namespace Mediator.SourceGenerator.Tests;

public sealed class BasicTests
{
[Fact]
public async Task Multiple_Request_Handlers_One_Class()
{
var inputCompilation = Fixture.CreateLibrary(
"""
using System;
using System.Threading;
using System.Threading.Tasks;
using Mediator;
using Microsoft.Extensions.DependencyInjection;

namespace TestCode;

public class Program
{
public static void Main()
{
var services = new ServiceCollection();

services.AddMediator();
}
}

public readonly record struct Request0(Guid Id) : IRequest<Response>;
public readonly record struct Request1(Guid Id) : IRequest<Response>;
public readonly record struct Response(Guid Id);
public sealed class RequestHandler : IRequestHandler<Request0, Response>, IRequestHandler<Request1, Response>
{
public ValueTask<Response> Handle(Request0 request, CancellationToken cancellationToken) =>
new ValueTask<Response>(new Response(request.Id));
public ValueTask<Response> Handle(Request1 request, CancellationToken cancellationToken) =>
new ValueTask<Response>(new Response(request.Id));
}
"""
);

await inputCompilation.AssertAndVerify(Assertions.CompilesWithoutDiagnostics);
}

[Fact]
public async Task Multiple_Notification_Handlers_One_Class()
{
var inputCompilation = Fixture.CreateLibrary(
"""
using System;
using System.Threading;
using System.Threading.Tasks;
using Mediator;
using Microsoft.Extensions.DependencyInjection;

namespace TestCode;

public class Program
{
public static void Main()
{
var services = new ServiceCollection();

services.AddMediator();
}
}

public readonly record struct Notification0() : INotification;
public readonly record struct Notification1() : INotification;
public sealed class RequestHandler : INotificationHandler<Notification0>, INotificationHandler<Notification1>
{
public ValueTask Handle(Notification0 request, CancellationToken cancellationToken) =>
default;
public ValueTask Handle(Notification1 request, CancellationToken cancellationToken) =>
default;
}
"""
);

await inputCompilation.AssertAndVerify(Assertions.CompilesWithoutDiagnostics);
}
}

Large diffs are not rendered by default.

Large diffs are not rendered by default.

148 changes: 148 additions & 0 deletions test/Mediator.Tests/MultipleHandlersTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace Mediator.Tests;

public class MultipleHandlersTests
{
public sealed record Request0(Guid Id) : IRequest<Response>;

public sealed record Request1(Guid Id) : IRequest<Response>;

public sealed record Request2(Guid Id) : IRequest<Response>;

public sealed record Request3(Guid Id) : IRequest<Response>;

public sealed record Response(Guid Id);

public sealed record Notification0(Guid Id) : INotification;

public sealed record Notification1(Guid Id) : INotification;

public sealed record Notification2(Guid Id) : INotification;

public sealed record Notification3(Guid Id) : INotification;

public sealed class NotificationHandler : INotificationHandler<Notification0>, INotificationHandler<Notification1>
{
public static readonly ConcurrentDictionary<Guid, int> InstanceIds = new();

public ValueTask Handle(Notification0 notification, CancellationToken cancellationToken)
{
InstanceIds.AddOrUpdate(notification.Id, 1, (_, count) => count + 1);
return default;
}

public ValueTask Handle(Notification1 notification, CancellationToken cancellationToken)
{
InstanceIds.AddOrUpdate(notification.Id, 1, (_, count) => count + 1);
return default;
}
}

public sealed class RequestHandler : IRequestHandler<Request0, Response>, IRequestHandler<Request1, Response>
{
public static readonly ConcurrentDictionary<Guid, int> InstanceIds = new();

public ValueTask<Response> Handle(Request0 request, CancellationToken cancellationToken)
{
InstanceIds.AddOrUpdate(request.Id, 1, (_, count) => count + 1);
return new ValueTask<Response>(new Response(request.Id));
}

public ValueTask<Response> Handle(Request1 request, CancellationToken cancellationToken)
{
InstanceIds.AddOrUpdate(request.Id, 1, (_, count) => count + 1);
return new ValueTask<Response>(new Response(request.Id));
}
}

public sealed class MultipleHandlers
: INotificationHandler<Notification2>,
INotificationHandler<Notification3>,
IRequestHandler<Request2, Response>,
IRequestHandler<Request3, Response>
{
public static readonly ConcurrentDictionary<Guid, int> InstanceIds = new();

public ValueTask Handle(Notification2 notification, CancellationToken cancellationToken)
{
InstanceIds.AddOrUpdate(notification.Id, 1, (_, count) => count + 1);
return default;
}

public ValueTask Handle(Notification3 notification, CancellationToken cancellationToken)
{
InstanceIds.AddOrUpdate(notification.Id, 1, (_, count) => count + 1);
return default;
}

public ValueTask<Response> Handle(Request2 request, CancellationToken cancellationToken)
{
InstanceIds.AddOrUpdate(request.Id, 1, (_, count) => count + 1);
return new ValueTask<Response>(new Response(request.Id));
}

public ValueTask<Response> Handle(Request3 request, CancellationToken cancellationToken)
{
InstanceIds.AddOrUpdate(request.Id, 1, (_, count) => count + 1);
return new ValueTask<Response>(new Response(request.Id));
}
}

[Fact]
public async Task Multiple_Notification_Handlers_One_Class()
{
var (sp, mediator) = Fixture.GetMediator();

var id0 = Guid.NewGuid();
var id1 = Guid.NewGuid();

await mediator.Publish(new Notification0(id0));
await mediator.Publish(new Notification1(id1));

Assert.Equal(2, NotificationHandler.InstanceIds.Count);
Assert.Equal(1, NotificationHandler.InstanceIds[id0]);
Assert.Equal(1, NotificationHandler.InstanceIds[id1]);
}

[Fact]
public async Task Multiple_Request_Handlers_One_Class()
{
var (sp, mediator) = Fixture.GetMediator();

var id0 = Guid.NewGuid();
var id1 = Guid.NewGuid();

_ = await mediator.Send(new Request0(id0));
_ = await mediator.Send(new Request1(id1));

Assert.Equal(2, RequestHandler.InstanceIds.Count);
Assert.Equal(1, RequestHandler.InstanceIds[id0]);
Assert.Equal(1, RequestHandler.InstanceIds[id1]);
}

[Fact]
public async Task Multiple_Handlers_One_Class()
{
var (sp, mediator) = Fixture.GetMediator();

var id0 = Guid.NewGuid();
var id1 = Guid.NewGuid();
var id2 = Guid.NewGuid();
var id3 = Guid.NewGuid();

_ = await mediator.Send(new Request2(id0));
_ = await mediator.Send(new Request3(id1));
await mediator.Publish(new Notification2(id2));
await mediator.Publish(new Notification3(id3));

Assert.Equal(4, MultipleHandlers.InstanceIds.Count);
Assert.Equal(1, MultipleHandlers.InstanceIds[id0]);
Assert.Equal(1, MultipleHandlers.InstanceIds[id1]);
Assert.Equal(1, MultipleHandlers.InstanceIds[id2]);
Assert.Equal(1, MultipleHandlers.InstanceIds[id3]);
}
}