diff --git a/src/Mocha/src/Mocha.Mediator/Mediator.cs b/src/Mocha/src/Mocha.Mediator/Mediator.cs
index 83676d466ce..45898e8f6f5 100644
--- a/src/Mocha/src/Mocha.Mediator/Mediator.cs
+++ b/src/Mocha/src/Mocha.Mediator/Mediator.cs
@@ -137,6 +137,16 @@ private ValueTask PublishCoreAsync(
{
var pipelines = runtime.GetNotificationPipelines(messageType);
+ if (pipelines.IsEmpty)
+ {
+ if (notification is not INotification)
+ {
+ throw ThrowHelper.NotANotification(messageType);
+ }
+
+ return default;
+ }
+
if (pipelines.Length == 1)
{
return PublishSingle(pipelines[0], notification, messageType, cancellationToken);
diff --git a/src/Mocha/src/Mocha.Mediator/MediatorRuntime.cs b/src/Mocha/src/Mocha.Mediator/MediatorRuntime.cs
index 46e89e8be1d..d2261ab0b76 100644
--- a/src/Mocha/src/Mocha.Mediator/MediatorRuntime.cs
+++ b/src/Mocha/src/Mocha.Mediator/MediatorRuntime.cs
@@ -98,7 +98,8 @@ public void ReturnContext(MediatorContext context)
}
///
- /// Gets the compiled notification pipeline delegates for the specified notification type.
+ /// Gets the compiled notification pipeline delegates for the specified notification type,
+ /// or an empty array if no handler is registered for it.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ImmutableArray GetNotificationPipelines(Type notificationType)
@@ -108,6 +109,6 @@ public ImmutableArray GetNotificationPipelines(Type notificati
return pipelines;
}
- throw ThrowHelper.MissingNotificationPipeline(notificationType);
+ return [];
}
}
diff --git a/src/Mocha/src/Mocha.Mediator/ThrowHelper.cs b/src/Mocha/src/Mocha.Mediator/ThrowHelper.cs
index 0c6af56845c..63029d8f208 100644
--- a/src/Mocha/src/Mocha.Mediator/ThrowHelper.cs
+++ b/src/Mocha/src/Mocha.Mediator/ThrowHelper.cs
@@ -6,10 +6,11 @@ public static Exception MissingPipeline(Type messageType)
=> new InvalidOperationException(
$"No pipeline registered for message type {messageType}");
- public static Exception MissingNotificationPipeline(Type notificationType)
- => new InvalidOperationException(
- $"No notification pipeline registered for message type {notificationType}. "
- + "If this is a command or query, use SendAsync or QueryAsync instead.");
+ public static Exception NotANotification(Type type)
+ => new ArgumentException(
+ $"Type '{type}' does not implement {nameof(INotification)}. "
+ + "If this is a command or query, use SendAsync or QueryAsync instead.",
+ "notification");
public static Exception BeforeAndAfterConflict()
=> new ArgumentException(
diff --git a/src/Mocha/test/Mocha.Mediator.Tests/MediatorDispatchTests.cs b/src/Mocha/test/Mocha.Mediator.Tests/MediatorDispatchTests.cs
index 58ee179f081..1c964ed4904 100644
--- a/src/Mocha/test/Mocha.Mediator.Tests/MediatorDispatchTests.cs
+++ b/src/Mocha/test/Mocha.Mediator.Tests/MediatorDispatchTests.cs
@@ -162,9 +162,59 @@ await Assert.ThrowsAsync(
() => mediator.SendAsync(new DispatchCommand("missing"), TestContext.Current.CancellationToken).AsTask());
await Assert.ThrowsAsync(
() => mediator.QueryAsync(new DispatchQuery(1), TestContext.Current.CancellationToken).AsTask());
- await Assert.ThrowsAsync(
- () => mediator.PublishAsync(new DispatchNotification("missing"), TestContext.Current.CancellationToken)
- .AsTask());
+ }
+
+ [Fact]
+ public async Task PublishAsync_Should_Complete_When_NoNotificationHandlerRegistered()
+ {
+ // arrange
+ var sp = DispatchTestHelper.BuildProvider((_, _) => { });
+ using var scope = sp.CreateScope();
+ var mediator = scope.ServiceProvider.GetRequiredService();
+
+ // act
+ var task = mediator.PublishAsync(new DispatchNotification("unhandled"), TestContext.Current.CancellationToken);
+
+ // assert
+ Assert.True(task.IsCompletedSuccessfully);
+ await task;
+ }
+
+ [Fact]
+ public async Task PublishAsync_Should_Complete_When_UntypedNotificationHasNoHandler()
+ {
+ // arrange
+ var sp = DispatchTestHelper.BuildProvider((_, _) => { });
+ using var scope = sp.CreateScope();
+ var publisher = scope.ServiceProvider.GetRequiredService();
+
+ // act
+ var task = publisher.PublishAsync(
+ (object)new DispatchNotification("unhandled"),
+ TestContext.Current.CancellationToken);
+
+ // assert
+ Assert.True(task.IsCompletedSuccessfully);
+ await task;
+ }
+
+ [Fact]
+ public async Task PublishAsync_Should_ThrowArgumentException_When_ObjectIsNotANotification()
+ {
+ // arrange
+ var sp = DispatchTestHelper.BuildProvider((_, _) => { });
+ using var scope = sp.CreateScope();
+ var publisher = scope.ServiceProvider.GetRequiredService();
+
+ // act
+ var exception = await Assert.ThrowsAsync(
+ () => publisher.PublishAsync(new DispatchVoidCommand("x"), TestContext.Current.CancellationToken).AsTask());
+
+ // assert
+ Assert.Equal(
+ "Type 'Mocha.Mediator.Tests.DispatchVoidCommand' does not implement INotification. "
+ + "If this is a command or query, use SendAsync or QueryAsync instead. (Parameter 'notification')",
+ exception.Message);
}
[Fact]