diff --git a/src/Mocha/Mocha.slnx b/src/Mocha/Mocha.slnx
index 76b017298ef..95383168cee 100644
--- a/src/Mocha/Mocha.slnx
+++ b/src/Mocha/Mocha.slnx
@@ -16,6 +16,8 @@
+
+
diff --git a/src/Mocha/benchmarks/Directory.Packages.props b/src/Mocha/benchmarks/Directory.Packages.props
index 163a961330c..6ced7aee888 100644
--- a/src/Mocha/benchmarks/Directory.Packages.props
+++ b/src/Mocha/benchmarks/Directory.Packages.props
@@ -2,6 +2,9 @@
true
+
+
+
diff --git a/src/Mocha/benchmarks/Mocha.Mediator.Benchmarks/Program.cs b/src/Mocha/benchmarks/Mocha.Mediator.Benchmarks/Program.cs
index 98103c9af46..0ba0e169aec 100644
--- a/src/Mocha/benchmarks/Mocha.Mediator.Benchmarks/Program.cs
+++ b/src/Mocha/benchmarks/Mocha.Mediator.Benchmarks/Program.cs
@@ -7,6 +7,6 @@
var config = DefaultConfig.Instance
.AddJob(Job.Default.WithToolchain(
CsProjCoreToolchain.From(
- new NetCoreAppSettings("net9.0", null, ".NET 9.0"))));
+ new NetCoreAppSettings("net10.0", null, ".NET 10.0"))));
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);
diff --git a/src/Mocha/src/Mocha.Analyzers/AnalyzerReleases.Unshipped.md b/src/Mocha/src/Mocha.Analyzers/AnalyzerReleases.Unshipped.md
index 8c98066f5de..5b5c6b3f8ec 100644
--- a/src/Mocha/src/Mocha.Analyzers/AnalyzerReleases.Unshipped.md
+++ b/src/Mocha/src/Mocha.Analyzers/AnalyzerReleases.Unshipped.md
@@ -6,3 +6,4 @@ MO0001 | Mediator | Warning | Message type has no registered handler
MO0002 | Mediator | Error | Message type has multiple handlers
MO0003 | Mediator | Warning | Handler is abstract and will not be registered
MO0004 | Mediator | Info | Open generic message type cannot be dispatched
+MO0005 | Mediator | Error | Handler implements multiple mediator handler interfaces
diff --git a/src/Mocha/src/Mocha.Analyzers/Errors.cs b/src/Mocha/src/Mocha.Analyzers/Errors.cs
index c0231e3f398..84021f29a0f 100644
--- a/src/Mocha/src/Mocha.Analyzers/Errors.cs
+++ b/src/Mocha/src/Mocha.Analyzers/Errors.cs
@@ -67,4 +67,20 @@ public static class Errors
category: "Mediator",
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true);
+
+ ///
+ /// Gets the descriptor for MO0005: a handler type implements multiple mediator handler interfaces.
+ ///
+ ///
+ /// Reported as an error when a concrete class implements more than one of
+ /// ICommandHandler, IQueryHandler, or INotificationHandler.
+ /// A handler must implement exactly one mediator handler interface.
+ ///
+ public static readonly DiagnosticDescriptor MultipleHandlerInterfaces = new(
+ id: "MO0005",
+ title: "Handler implements multiple mediator handler interfaces",
+ messageFormat: "Handler '{0}' must implement exactly one mediator handler interface",
+ category: "Mediator",
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
}
diff --git a/src/Mocha/src/Mocha.Analyzers/FileBuilders/DependencyInjectionFileBuilder.cs b/src/Mocha/src/Mocha.Analyzers/FileBuilders/DependencyInjectionFileBuilder.cs
index 2cbe36e7c98..f8e520c70e2 100644
--- a/src/Mocha/src/Mocha.Analyzers/FileBuilders/DependencyInjectionFileBuilder.cs
+++ b/src/Mocha/src/Mocha.Analyzers/FileBuilders/DependencyInjectionFileBuilder.cs
@@ -42,102 +42,65 @@ public void WriteBeginRegistrationMethod()
Writer.DecreaseIndent();
Writer.WriteIndentedLine("{");
Writer.IncreaseIndent();
-
- Writer.WriteIndentedLine("var services = builder.Services;");
- Writer.WriteIndentedLine("var lifetime = builder.Options.ServiceLifetime;");
- }
-
- public void WriteHandlerRegistration(HandlerInfo handler)
- {
- switch (handler.Kind)
- {
- case HandlerKind.CommandVoid:
- WriteServiceDescriptor(
- "global::Mocha.Mediator.ICommandHandler<{0}>",
- handler.HandlerTypeName,
- handler.MessageTypeName);
- break;
- case HandlerKind.CommandResponse:
- WriteServiceDescriptor(
- "global::Mocha.Mediator.ICommandHandler<{0}, {1}>",
- handler.HandlerTypeName,
- handler.MessageTypeName,
- handler.ResponseTypeName!);
- break;
- case HandlerKind.Query:
- WriteServiceDescriptor(
- "global::Mocha.Mediator.IQueryHandler<{0}, {1}>",
- handler.HandlerTypeName,
- handler.MessageTypeName,
- handler.ResponseTypeName!);
- break;
- }
- }
-
- public void WriteNotificationHandlerRegistration(NotificationHandlerInfo handler)
- {
- // Use TryAddEnumerable to prevent duplicate handler registrations
- // when the generated Add{Module} extension is called more than once
- // (e.g. in tests or modular startup).
- Writer.WriteIndentedLine(
- "global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddEnumerable(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.INotificationHandler<{0}>), typeof({1}), lifetime));",
- handler.NotificationTypeName,
- handler.HandlerTypeName);
- }
-
- ///
- /// Writes the opening of a ConfigureMediator lambda for deferred pipeline registrations.
- ///
- public void WriteBeginConfigureMediator()
- {
- Writer.WriteIndentedLine("global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>");
- Writer.WriteIndentedLine("{");
- Writer.IncreaseIndent();
- }
-
- ///
- /// Writes the closing of a ConfigureMediator lambda.
- ///
- public void WriteEndConfigureMediator()
- {
- Writer.DecreaseIndent();
- Writer.WriteIndentedLine("});");
}
///
- /// Writes a pipeline registration for a handler (inside ConfigureMediator lambda, using 'b').
+ /// Writes an AddHandlerConfiguration call for a command/query handler.
///
- public void WritePipelineRegistration(HandlerInfo handler)
+ public void WriteHandlerConfiguration(HandlerInfo handler)
{
- var (terminalMethod, responseType) = handler.Kind switch
+ var (kindEnum, terminalCall) = handler.Kind switch
{
- HandlerKind.CommandVoid => ($"BuildVoidCommandTerminal<{handler.MessageTypeName}>()", null),
- HandlerKind.CommandResponse => ($"BuildCommandTerminal<{handler.MessageTypeName}, {handler.ResponseTypeName}>()", handler.ResponseTypeName),
- HandlerKind.Query => ($"BuildQueryTerminal<{handler.MessageTypeName}, {handler.ResponseTypeName}>()", handler.ResponseTypeName),
+ HandlerKind.Command => (
+ "Command",
+ $"BuildCommandPipeline<{handler.HandlerTypeName}, {handler.MessageTypeName}>()"),
+ HandlerKind.CommandResponse => (
+ "CommandResponse",
+ $"BuildCommandResponsePipeline<{handler.HandlerTypeName}, {handler.MessageTypeName}, {handler.ResponseTypeName}>()"),
+ HandlerKind.Query => (
+ "Query",
+ $"BuildQueryPipeline<{handler.HandlerTypeName}, {handler.MessageTypeName}, {handler.ResponseTypeName}>()"),
_ => throw new ArgumentOutOfRangeException()
};
- WritePipelineConfiguration(handler.MessageTypeName, responseType, terminalMethod);
+ WriteAddHandlerConfiguration(
+ handler.HandlerTypeName,
+ handler.MessageTypeName,
+ handler.ResponseTypeName,
+ kindEnum,
+ $"global::Mocha.Mediator.PipelineBuilder.{terminalCall}");
}
///
- /// Writes a pipeline registration for a notification group (inside ConfigureMediator lambda, using 'b').
+ /// Writes an AddHandlerConfiguration call for a notification handler.
///
- public void WriteNotificationPipelineRegistration(string notificationType,
- List groupHandlers)
+ public void WriteNotificationHandlerConfiguration(
+ string notificationType,
+ NotificationHandlerInfo handler)
{
- var handlerTypeArgs = string.Join(", ",
- groupHandlers.Select(h => $"typeof({h.HandlerTypeName})"));
-
- var terminalMethod = $"BuildNotificationTerminal<{notificationType}>(new global::System.Type[] {{ {handlerTypeArgs} }})";
- WritePipelineConfiguration(notificationType, null, terminalMethod);
+ WriteAddHandlerConfiguration(
+ handler.HandlerTypeName,
+ notificationType,
+ responseTypeName: null,
+ "Notification",
+ $"global::Mocha.Mediator.PipelineBuilder.BuildNotificationPipeline<{handler.HandlerTypeName}, {notificationType}>()");
}
- private void WritePipelineConfiguration(string messageTypeName, string? responseTypeName, string terminalMethod)
+ private void WriteAddHandlerConfiguration(
+ string handlerTypeName,
+ string messageTypeName,
+ string? responseTypeName,
+ string kindEnum,
+ string terminalCall)
{
- Writer.WriteIndentedLine("b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration");
+ Writer.WriteIndentedLine(
+ "global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration<{0}>(builder,",
+ handlerTypeName);
+ Writer.IncreaseIndent();
+ Writer.WriteIndentedLine("new global::Mocha.Mediator.MediatorHandlerConfiguration");
Writer.WriteIndentedLine("{");
Writer.IncreaseIndent();
+ Writer.WriteIndentedLine("HandlerType = typeof({0}),", handlerTypeName);
Writer.WriteIndentedLine("MessageType = typeof({0}),", messageTypeName);
if (responseTypeName is not null)
@@ -145,9 +108,11 @@ private void WritePipelineConfiguration(string messageTypeName, string? response
Writer.WriteIndentedLine("ResponseType = typeof({0}),", responseTypeName);
}
- Writer.WriteIndentedLine("Terminal = global::Mocha.Mediator.PipelineBuilder.{0}", terminalMethod);
+ Writer.WriteIndentedLine("Kind = global::Mocha.Mediator.MediatorHandlerKind.{0},", kindEnum);
+ Writer.WriteIndentedLine("Delegate = {0}", terminalCall);
Writer.DecreaseIndent();
Writer.WriteIndentedLine("});");
+ Writer.DecreaseIndent();
}
public void WriteSectionComment(string comment)
@@ -164,17 +129,6 @@ public void WriteEndRegistrationMethod()
Writer.WriteIndentedLine("}");
}
- private void WriteServiceDescriptor(string serviceTypeFormat, string implementationType, params object[] typeArgs)
- {
- // Use TryAdd to prevent duplicate handler registrations
- // when the generated Add{Module} extension is called more than once.
- var serviceType = string.Format(serviceTypeFormat, typeArgs);
- Writer.WriteIndentedLine(
- "global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof({0}), typeof({1}), lifetime));",
- serviceType,
- implementationType);
- }
-
#pragma warning disable CA5351 // MD5 is used for non-security hashing (file name salting)
private static readonly MD5 s_md5 = MD5.Create();
#pragma warning restore CA5351
diff --git a/src/Mocha/src/Mocha.Analyzers/Generators/DependencyInjectionGenerator.cs b/src/Mocha/src/Mocha.Analyzers/Generators/DependencyInjectionGenerator.cs
index 53e2193b727..197cff1c0c9 100644
--- a/src/Mocha/src/Mocha.Analyzers/Generators/DependencyInjectionGenerator.cs
+++ b/src/Mocha/src/Mocha.Analyzers/Generators/DependencyInjectionGenerator.cs
@@ -37,57 +37,33 @@ public void Generate(
using var builder = new DependencyInjectionFileBuilder(moduleName, assemblyName);
- builder.WriteHeader();
- builder.WriteBeginNamespace();
- builder.WriteBeginClass();
- builder.WriteBeginRegistrationMethod();
-
- // Register handlers
- if (handlers.Count > 0)
- {
- builder.WriteSectionComment("Register handlers");
-
- foreach (var handler in handlers)
- {
- builder.WriteHandlerRegistration(handler);
- }
- }
-
- // Register notification handlers
- if (notificationHandlers.Count > 0)
- {
- builder.WriteSectionComment("Register notification handlers");
-
- foreach (var handler in notificationHandlers)
- {
- builder.WriteNotificationHandlerRegistration(handler);
- }
- }
-
- // Register pipelines (all handlers + notifications) via deferred ConfigureMediator
var notificationGroups = notificationHandlers
.GroupBy(h => h.NotificationTypeName)
.OrderBy(g => g.Key)
.ToList();
+ builder.WriteHeader();
+ builder.WriteBeginNamespace();
+ builder.WriteBeginClass();
+ builder.WriteBeginRegistrationMethod();
+
+ // Register all handler configurations
if (handlers.Count > 0 || notificationGroups.Count > 0)
{
- builder.WriteSectionComment("Register pipelines");
- builder.WriteBeginConfigureMediator();
+ builder.WriteSectionComment("Register handler configurations");
foreach (var handler in handlers)
{
- builder.WritePipelineRegistration(handler);
+ builder.WriteHandlerConfiguration(handler);
}
foreach (var group in notificationGroups)
{
- builder.WriteNotificationPipelineRegistration(
- group.Key,
- group.OrderBy(h => h.HandlerTypeName).ToList());
+ foreach (var handler in group.OrderBy(h => h.HandlerTypeName))
+ {
+ builder.WriteNotificationHandlerConfiguration(group.Key, handler);
+ }
}
-
- builder.WriteEndConfigureMediator();
}
builder.WriteEndRegistrationMethod();
diff --git a/src/Mocha/src/Mocha.Analyzers/Inspectors/HandlerInspector.cs b/src/Mocha/src/Mocha.Analyzers/Inspectors/HandlerInspector.cs
index d3989d8397b..9e6d933551b 100644
--- a/src/Mocha/src/Mocha.Analyzers/Inspectors/HandlerInspector.cs
+++ b/src/Mocha/src/Mocha.Analyzers/Inspectors/HandlerInspector.cs
@@ -23,7 +23,7 @@ public sealed class HandlerInspector : ISyntaxInspector
private static readonly HandlerKindDescriptor[] s_handlerKinds =
[
- new(static s => s.ICommandHandlerVoid, HandlerKind.CommandVoid, HasResponse: false),
+ new(static s => s.ICommandHandlerVoid, HandlerKind.Command, HasResponse: false),
new(static s => s.ICommandHandlerResponse, HandlerKind.CommandResponse, HasResponse: true),
new(static s => s.IQueryHandler, HandlerKind.Query, HasResponse: true)
];
diff --git a/src/Mocha/src/Mocha.Analyzers/Inspectors/MessageTypeInspector.cs b/src/Mocha/src/Mocha.Analyzers/Inspectors/MessageTypeInspector.cs
index 19e5ad0bb35..b2ec4e3e74f 100644
--- a/src/Mocha/src/Mocha.Analyzers/Inspectors/MessageTypeInspector.cs
+++ b/src/Mocha/src/Mocha.Analyzers/Inspectors/MessageTypeInspector.cs
@@ -98,7 +98,7 @@ public bool TryHandle(
return true;
}
- syntaxInfo = new MessageTypeInfo(typeName, typeNamespace, MessageKind.CommandVoid, location);
+ syntaxInfo = new MessageTypeInfo(typeName, typeNamespace, MessageKind.Command, location);
return true;
}
diff --git a/src/Mocha/src/Mocha.Analyzers/Inspectors/MultipleHandlerInterfaceInspector.cs b/src/Mocha/src/Mocha.Analyzers/Inspectors/MultipleHandlerInterfaceInspector.cs
new file mode 100644
index 00000000000..02dbf9eda34
--- /dev/null
+++ b/src/Mocha/src/Mocha.Analyzers/Inspectors/MultipleHandlerInterfaceInspector.cs
@@ -0,0 +1,88 @@
+using System.Collections.Immutable;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Mocha.Analyzers.Filters;
+using Mocha.Analyzers.Utils;
+
+namespace Mocha.Analyzers.Inspectors;
+
+public sealed class MultipleHandlerInterfaceInspector : ISyntaxInspector
+{
+ public ImmutableArray Filters { get; } = [ClassWithMochaBaseListFilter.Instance];
+
+ public IImmutableSet SupportedKinds { get; } =
+ ImmutableHashSet.Create(SyntaxKind.ClassDeclaration, SyntaxKind.RecordDeclaration);
+
+ public bool TryHandle(
+ KnownTypeSymbols knownSymbols,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ CancellationToken cancellationToken,
+ out SyntaxInfo? syntaxInfo)
+ {
+ if (node is not TypeDeclarationSyntax typeDeclaration)
+ {
+ syntaxInfo = null;
+ return false;
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var typeSymbol = semanticModel.GetDeclaredSymbol(typeDeclaration);
+
+ if (typeSymbol is null
+ || typeSymbol.IsAbstract
+ || typeSymbol.TypeKind == TypeKind.Interface)
+ {
+ syntaxInfo = null;
+ return false;
+ }
+
+ var count = 0;
+
+ foreach (var @interface in typeSymbol.Interfaces)
+ {
+ var original = @interface.OriginalDefinition;
+
+ if (SymbolEqualityComparer.Default.Equals(original, knownSymbols.ICommandHandlerVoid)
+ || SymbolEqualityComparer.Default.Equals(original, knownSymbols.ICommandHandlerResponse)
+ || SymbolEqualityComparer.Default.Equals(original, knownSymbols.IQueryHandler)
+ || SymbolEqualityComparer.Default.Equals(original, knownSymbols.INotificationHandler))
+ {
+ count++;
+
+ if (count > 1)
+ {
+ break;
+ }
+ }
+ }
+
+ if (count <= 1)
+ {
+ syntaxInfo = null;
+ return false;
+ }
+
+ var handlerName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ var locationInfo = typeDeclaration.Identifier.GetLocation().ToLocationInfo();
+
+ syntaxInfo = new MultipleHandlerInterfaceDiagnosticInfo(handlerName)
+ {
+ Diagnostics = new([
+ new DiagnosticInfo(
+ Errors.MultipleHandlerInterfaces.Id,
+ locationInfo,
+ new([handlerName]))
+ ])
+ };
+ return true;
+ }
+}
+
+internal sealed record MultipleHandlerInterfaceDiagnosticInfo(string HandlerTypeName) : SyntaxInfo
+{
+ public override string OrderByKey => $"MultipleHandlerDiag:{HandlerTypeName}";
+}
diff --git a/src/Mocha/src/Mocha.Analyzers/MediatorGenerator.cs b/src/Mocha/src/Mocha.Analyzers/MediatorGenerator.cs
index c43a4576e4b..85d11bdfe62 100644
--- a/src/Mocha/src/Mocha.Analyzers/MediatorGenerator.cs
+++ b/src/Mocha/src/Mocha.Analyzers/MediatorGenerator.cs
@@ -21,6 +21,7 @@ public sealed class MediatorGenerator : IIncrementalGenerator
{
private static readonly ISyntaxInspector[] s_allInspectors =
[
+ new MultipleHandlerInterfaceInspector(),
new HandlerInspector(),
new NotificationHandlerInspector(),
new MessageTypeInspector(),
@@ -244,7 +245,8 @@ private static void ValidateMessageHandlerPairing(
[Errors.MissingHandler.Id] = Errors.MissingHandler,
[Errors.DuplicateHandler.Id] = Errors.DuplicateHandler,
[Errors.AbstractHandler.Id] = Errors.AbstractHandler,
- [Errors.OpenGenericMessageType.Id] = Errors.OpenGenericMessageType
+ [Errors.OpenGenericMessageType.Id] = Errors.OpenGenericMessageType,
+ [Errors.MultipleHandlerInterfaces.Id] = Errors.MultipleHandlerInterfaces
};
private static Diagnostic ReconstructDiagnostic(DiagnosticInfo info)
diff --git a/src/Mocha/src/Mocha.Analyzers/Models/HandlerKind.cs b/src/Mocha/src/Mocha.Analyzers/Models/HandlerKind.cs
index 8093c8e1b56..525fefcc8f1 100644
--- a/src/Mocha/src/Mocha.Analyzers/Models/HandlerKind.cs
+++ b/src/Mocha/src/Mocha.Analyzers/Models/HandlerKind.cs
@@ -8,7 +8,7 @@ public enum HandlerKind
///
/// A command handler that returns no response.
///
- CommandVoid,
+ Command,
///
/// A command handler that returns a response.
diff --git a/src/Mocha/src/Mocha.Analyzers/Models/MessageKind.cs b/src/Mocha/src/Mocha.Analyzers/Models/MessageKind.cs
index ed34c42613e..69435715d4d 100644
--- a/src/Mocha/src/Mocha.Analyzers/Models/MessageKind.cs
+++ b/src/Mocha/src/Mocha.Analyzers/Models/MessageKind.cs
@@ -8,7 +8,7 @@ public enum MessageKind
///
/// A void command (no response).
///
- CommandVoid,
+ Command,
///
/// A command that returns a response.
diff --git a/src/Mocha/src/Mocha.Mediator.Abstractions/INotificationStrategy.cs b/src/Mocha/src/Mocha.Mediator.Abstractions/INotificationStrategy.cs
deleted file mode 100644
index c14decf82ea..00000000000
--- a/src/Mocha/src/Mocha.Mediator.Abstractions/INotificationStrategy.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-namespace Mocha.Mediator;
-
-///
-/// Defines the strategy for dispatching notifications to multiple handlers.
-///
-///
-/// Implementations control how handlers are invoked (e.g., sequentially, in parallel, or with specific error handling).
-///
-public interface INotificationStrategy
-{
- ///
- /// Publishes a notification to all of the provided handlers using this strategy.
- ///
- /// The type of notification to publish.
- /// The collection of handlers to dispatch the notification to.
- /// The notification to publish.
- /// A token that may be used to cancel the asynchronous operation.
- /// A representing the asynchronous operation.
- ValueTask PublishAsync(
- IReadOnlyList> handlers,
- TNotification notification,
- CancellationToken cancellationToken)
- where TNotification : INotification;
-}
diff --git a/src/Mocha/src/Mocha.Mediator/Configuration/MediatorConfiguration.cs b/src/Mocha/src/Mocha.Mediator/Configuration/MediatorConfiguration.cs
new file mode 100644
index 00000000000..d42c2ed0cbb
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Configuration/MediatorConfiguration.cs
@@ -0,0 +1,24 @@
+using Mocha.Features;
+
+namespace Mocha.Mediator;
+
+///
+/// Base class for mediator configuration objects that support feature-based extensibility through
+/// .
+///
+public abstract class MediatorConfiguration : IFeatureProvider
+{
+ private IFeatureCollection? _features;
+
+ ///
+ /// Get access to context data that are copied to the type
+ /// and can be used for customizations.
+ ///
+ public virtual IFeatureCollection Features => _features ??= new FeatureCollection();
+
+ ///
+ /// Get access to features that are copied to the type
+ /// and can be used for customizations.
+ ///
+ public IFeatureCollection GetFeatures() => _features ?? FeatureCollection.Empty;
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Configuration/MediatorHandlerConfiguration.cs b/src/Mocha/src/Mocha.Mediator/Configuration/MediatorHandlerConfiguration.cs
new file mode 100644
index 00000000000..430e626060c
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Configuration/MediatorHandlerConfiguration.cs
@@ -0,0 +1,37 @@
+namespace Mocha.Mediator;
+
+///
+/// Holds the resolved configuration for a mediator handler, including its type metadata,
+/// handler kind, and an optional pre-built terminal delegate.
+///
+public class MediatorHandlerConfiguration : MediatorConfiguration
+{
+ ///
+ /// Gets or sets the concrete handler implementation type.
+ ///
+ public Type? HandlerType { get; set; }
+
+ ///
+ /// Gets or sets the message type (command, query, or notification type).
+ ///
+ public Type? MessageType { get; set; }
+
+ ///
+ /// Gets or sets the response type, or null for void commands and notifications.
+ ///
+ public Type? ResponseType { get; set; }
+
+ ///
+ /// Gets or sets the kind of handler.
+ ///
+ public MediatorHandlerKind Kind { get; set; }
+
+ ///
+ /// Gets or sets an optional pre-built delegate. When set, the builder uses this
+ /// directly (AOT-safe, source-generator path). When null, the builder creates the delegate
+ /// via reflection (manual AddHandler path).
+ /// This is the innermost delegate that resolves and invokes the handler.
+ /// It is wrapped in middleware during pipeline compilation.
+ ///
+ public MediatorDelegate? Delegate { get; set; }
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Configuration/MediatorHandlerKind.cs b/src/Mocha/src/Mocha.Mediator/Configuration/MediatorHandlerKind.cs
new file mode 100644
index 00000000000..5ffa7ae60ab
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Configuration/MediatorHandlerKind.cs
@@ -0,0 +1,27 @@
+namespace Mocha.Mediator;
+
+///
+/// Specifies the kind of mediator handler.
+///
+public enum MediatorHandlerKind
+{
+ ///
+ /// A command handler that does not return a response.
+ ///
+ Command,
+
+ ///
+ /// A command handler that returns a response.
+ ///
+ CommandResponse,
+
+ ///
+ /// A query handler that returns a response.
+ ///
+ Query,
+
+ ///
+ /// A notification handler. Multiple handlers can be registered for the same notification type.
+ ///
+ Notification
+}
diff --git a/src/Mocha/src/Mocha.Mediator/DependencyInjection/IMediatorBuilder.cs b/src/Mocha/src/Mocha.Mediator/DependencyInjection/IMediatorBuilder.cs
index 81a71c7f94d..5d09f817bf9 100644
--- a/src/Mocha/src/Mocha.Mediator/DependencyInjection/IMediatorBuilder.cs
+++ b/src/Mocha/src/Mocha.Mediator/DependencyInjection/IMediatorBuilder.cs
@@ -41,9 +41,16 @@ public interface IMediatorBuilder
IMediatorBuilder ConfigureServices(Action configure);
///
- /// Registers a pipeline configuration for the specified message type.
+ /// Registers a handler via descriptor-based configuration.
+ ///
+ /// The handler implementation type.
+ /// An optional action to configure the handler descriptor.
+ void AddHandler(Action? configure = null) where THandler : class;
+
+ ///
+ /// Registers a handler with a pre-built configuration.
/// This method is intended for use by source-generated code.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- void RegisterPipeline(MediatorPipelineConfiguration configuration);
+ void AddHandlerConfiguration(MediatorHandlerConfiguration configuration);
}
diff --git a/src/Mocha/src/Mocha.Mediator/DependencyInjection/MediatorBuilder.cs b/src/Mocha/src/Mocha.Mediator/DependencyInjection/MediatorBuilder.cs
index fbe7e73f89b..59144942e17 100644
--- a/src/Mocha/src/Mocha.Mediator/DependencyInjection/MediatorBuilder.cs
+++ b/src/Mocha/src/Mocha.Mediator/DependencyInjection/MediatorBuilder.cs
@@ -1,4 +1,7 @@
using System.Collections.Frozen;
+using System.Collections.Immutable;
+using System.ComponentModel;
+using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Mocha.Features;
@@ -12,13 +15,25 @@ namespace Mocha.Mediator;
///
public sealed class MediatorBuilder : IMediatorBuilder
{
+ private static readonly MethodInfo s_buildCommandPipeline =
+ typeof(PipelineBuilder).GetMethod(nameof(PipelineBuilder.BuildCommandPipeline))!;
+
+ private static readonly MethodInfo s_buildCommandResponsePipeline =
+ typeof(PipelineBuilder).GetMethod(nameof(PipelineBuilder.BuildCommandResponsePipeline))!;
+
+ private static readonly MethodInfo s_buildQueryPipeline =
+ typeof(PipelineBuilder).GetMethod(nameof(PipelineBuilder.BuildQueryPipeline))!;
+
+ private static readonly MethodInfo s_buildNotificationPipeline =
+ typeof(PipelineBuilder).GetMethod(nameof(PipelineBuilder.BuildNotificationPipeline))!;
+
private readonly List _middlewares =
[
MediatorDiagnosticMiddleware.Create()
];
private readonly List>> _pipelineModifiers = [];
- private readonly List _pipelines = [];
+ private readonly Dictionary> _handlerDescriptors = [];
private readonly List> _configureServices = [];
private readonly List> _configureFeatures = [];
private readonly MediatorOptions _options = new();
@@ -39,8 +54,7 @@ public IMediatorBuilder Use(MediatorMiddlewareConfiguration middleware, string?
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
@@ -57,8 +71,7 @@ public IMediatorBuilder Use(MediatorMiddlewareConfiguration middleware, string?
if (index == -1)
{
- throw new InvalidOperationException(
- $"The middleware with the key `{anchor}` was not found.");
+ throw ThrowHelper.MiddlewareKeyNotFound(anchor);
}
pipeline.Insert(before is not null ? index : index + 1, middleware);
@@ -93,11 +106,63 @@ public IMediatorBuilder ConfigureServices(Action
- public void RegisterPipeline(MediatorPipelineConfiguration configuration)
+ public void AddHandler(Action? configure = null)
+ where THandler : class
+ {
+ var handlerType = typeof(THandler);
+ var existing = _handlerDescriptors.GetValueOrDefault(handlerType);
+
+ if (existing is not null && configure is not null)
+ {
+ var inner = existing;
+ _handlerDescriptors[handlerType] = d =>
+ {
+ inner(d);
+ configure(d);
+ };
+ }
+ else if (configure is not null)
+ {
+ _handlerDescriptors[handlerType] = configure;
+ }
+ else
+ {
+ _handlerDescriptors.TryAdd(handlerType, static _ => { });
+ }
+ }
+
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void AddHandlerConfiguration(MediatorHandlerConfiguration configuration)
{
ArgumentNullException.ThrowIfNull(configuration);
- _pipelines.Add(configuration);
+ var handlerType = configuration.HandlerType!;
+
+ void ApplyConfig(MediatorHandlerDescriptor d)
+ {
+ var config = d.Extend().Configuration;
+ config.MessageType = configuration.MessageType;
+ config.ResponseType = configuration.ResponseType;
+ config.Kind = configuration.Kind;
+ config.Delegate = configuration.Delegate;
+ }
+
+ var existing = _handlerDescriptors.GetValueOrDefault(handlerType);
+
+ if (existing is not null)
+ {
+ var inner = existing;
+ _handlerDescriptors[handlerType] = d =>
+ {
+ inner(d);
+ ApplyConfig(d);
+ };
+ }
+ else
+ {
+ _handlerDescriptors[handlerType] = ApplyConfig;
+ }
}
///
@@ -131,12 +196,8 @@ public MediatorRuntime Build(IServiceProvider applicationServices)
configure(features);
}
- if (!features.TryGet(out _))
- {
- var strategy = applicationServices.GetService()
- ?? new ForeachAwaitPublisher();
- features.Set(new NotificationStrategyFeature(strategy));
- }
+ // Create the configuration context for descriptor construction.
+ var configContext = new MediatorConfigurationContext(_options, internalProvider, features);
// Compile pipelines using the internal provider for middleware factory context.
var factoryCtx = new MediatorMiddlewareFactoryContext { Services = internalProvider, Features = features };
@@ -149,20 +210,92 @@ public MediatorRuntime Build(IServiceProvider applicationServices)
? new IReadOnlyList>>[] { _pipelineModifiers }
: [];
- var pipelines = new Dictionary(_pipelines.Count);
+ var pipelines = new Dictionary();
+ var notificationTerminals = new Dictionary>();
+
+ foreach (var (handlerType, configureDelegate) in _handlerDescriptors)
+ {
+ var descriptor = new MediatorHandlerDescriptor(configContext, handlerType);
+ configureDelegate(descriptor);
+ var config = descriptor.CreateConfiguration();
+
+ var terminal = config.Delegate ?? BuildPipelineViaReflection(config);
+
+ if (config.Kind == MediatorHandlerKind.Notification)
+ {
+ if (!notificationTerminals.TryGetValue(config.MessageType!, out var list))
+ {
+ list = [];
+ notificationTerminals[config.MessageType!] = list;
+ }
+
+ list.Add(terminal);
+ }
+ else
+ {
+ factoryCtx.MessageType = config.MessageType!;
+ factoryCtx.ResponseType = config.ResponseType;
+
+ pipelines[config.MessageType!] =
+ MediatorMiddlewareCompiler.Compile(factoryCtx, terminal, middlewareConfigs, modifiers);
+ }
+ }
+
+ // Compile notification pipelines - each handler terminal is independently
+ // wrapped in middleware, producing a MediatorDelegate[] per notification type.
+ var notificationPipelines = new Dictionary>(notificationTerminals.Count);
- foreach (var config in _pipelines)
+ foreach (var (notificationType, terminals) in notificationTerminals)
{
- factoryCtx.MessageType = config.MessageType;
- factoryCtx.ResponseType = config.ResponseType;
+ factoryCtx.MessageType = notificationType;
+ factoryCtx.ResponseType = null;
+
+ var compiled = ImmutableArray.CreateBuilder(terminals.Count);
+ for (var i = 0; i < terminals.Count; i++)
+ {
+ compiled.Add(MediatorMiddlewareCompiler.Compile(
+ factoryCtx, terminals[i], middlewareConfigs, modifiers));
+ }
- pipelines[config.MessageType] =
- MediatorMiddlewareCompiler.Compile(factoryCtx, config.Terminal, middlewareConfigs, modifiers);
+ notificationPipelines[notificationType] = compiled.ToImmutable();
}
var pools = applicationServices.GetRequiredService();
- return new MediatorRuntime(pipelines.ToFrozenDictionary(), pools, features);
+ return new MediatorRuntime(
+ pipelines.ToFrozenDictionary(),
+ notificationPipelines.ToFrozenDictionary(),
+ pools,
+ features,
+ _options.NotificationPublishMode);
+ }
+
+ private static MediatorDelegate BuildPipelineViaReflection(MediatorHandlerConfiguration config)
+ {
+ return config.Kind switch
+ {
+ MediatorHandlerKind.Command =>
+ (MediatorDelegate)s_buildCommandPipeline
+ .MakeGenericMethod(config.HandlerType!, config.MessageType!)
+ .Invoke(null, null)!,
+
+ MediatorHandlerKind.CommandResponse =>
+ (MediatorDelegate)s_buildCommandResponsePipeline
+ .MakeGenericMethod(config.HandlerType!, config.MessageType!, config.ResponseType!)
+ .Invoke(null, null)!,
+
+ MediatorHandlerKind.Query =>
+ (MediatorDelegate)s_buildQueryPipeline
+ .MakeGenericMethod(config.HandlerType!, config.MessageType!, config.ResponseType!)
+ .Invoke(null, null)!,
+
+ MediatorHandlerKind.Notification =>
+ (MediatorDelegate)s_buildNotificationPipeline
+ .MakeGenericMethod(config.HandlerType!, config.MessageType!)
+ .Invoke(null, null)!,
+
+ _ => throw ThrowHelper.UnknownHandlerKind(config.Kind)
+ };
}
///
diff --git a/src/Mocha/src/Mocha.Mediator/DependencyInjection/MediatorHostBuilderHandlerExtensions.cs b/src/Mocha/src/Mocha.Mediator/DependencyInjection/MediatorHostBuilderHandlerExtensions.cs
new file mode 100644
index 00000000000..5fea8ccd6b9
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/DependencyInjection/MediatorHostBuilderHandlerExtensions.cs
@@ -0,0 +1,61 @@
+using System.ComponentModel;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+
+namespace Mocha.Mediator;
+
+///
+/// Provides extension methods for registering mediator handlers through the host builder.
+///
+public static class MediatorHostBuilderHandlerExtensions
+{
+ ///
+ /// Registers a handler with the mediator using descriptor-based configuration.
+ /// The handler type is inspected for ,
+ /// ,
+ /// , or
+ /// interfaces
+ /// and the appropriate pipeline is configured automatically.
+ ///
+ /// The handler implementation type.
+ /// The mediator host builder.
+ /// An optional action to configure the handler descriptor.
+ public static IMediatorHostBuilder AddHandler(
+ this IMediatorHostBuilder builder,
+ Action? configure = null)
+ where THandler : class
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ builder.Services.TryAdd(new ServiceDescriptor(
+ typeof(THandler), typeof(THandler), builder.Options.ServiceLifetime));
+
+ builder.ConfigureMediator(b => b.AddHandler(configure));
+
+ return builder;
+ }
+
+ ///
+ /// Registers a handler with the mediator using a pre-built configuration.
+ /// This method is intended for use by source-generated code.
+ ///
+ /// The handler implementation type.
+ /// The mediator host builder.
+ /// The pre-built handler configuration.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static IMediatorHostBuilder AddHandlerConfiguration(
+ this IMediatorHostBuilder builder,
+ MediatorHandlerConfiguration configuration)
+ where THandler : class
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+ ArgumentNullException.ThrowIfNull(configuration);
+
+ builder.Services.TryAdd(new ServiceDescriptor(
+ typeof(THandler), typeof(THandler), builder.Options.ServiceLifetime));
+
+ builder.ConfigureMediator(b => b.AddHandlerConfiguration(configuration));
+
+ return builder;
+ }
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Descriptors/IHasMediatorConfigurationContext.cs b/src/Mocha/src/Mocha.Mediator/Descriptors/IHasMediatorConfigurationContext.cs
new file mode 100644
index 00000000000..b9d4ad2e7bf
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Descriptors/IHasMediatorConfigurationContext.cs
@@ -0,0 +1,12 @@
+namespace Mocha.Mediator;
+
+///
+/// Indicates that a descriptor or extension has access to the mediator configuration context.
+///
+public interface IHasMediatorConfigurationContext
+{
+ ///
+ /// The descriptor context.
+ ///
+ IMediatorConfigurationContext Context { get; }
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorConfigurationContext.cs b/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorConfigurationContext.cs
new file mode 100644
index 00000000000..4ede7614b2f
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorConfigurationContext.cs
@@ -0,0 +1,17 @@
+namespace Mocha.Mediator;
+
+///
+/// Provides access to mediator configuration state during descriptor construction.
+///
+public interface IMediatorConfigurationContext : IFeatureProvider
+{
+ ///
+ /// Gets the service provider used for resolving configuration-time dependencies.
+ ///
+ IServiceProvider Services { get; }
+
+ ///
+ /// Gets the mediator options.
+ ///
+ MediatorOptions Options { get; }
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorDescriptor.cs b/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorDescriptor.cs
new file mode 100644
index 00000000000..92775360095
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorDescriptor.cs
@@ -0,0 +1,36 @@
+namespace Mocha.Mediator;
+
+///
+/// A typed mediator descriptor that provides access to extension points for the underlying configuration.
+///
+/// The configuration type managed by this descriptor.
+public interface IMediatorDescriptor : IMediatorDescriptor where T : MediatorConfiguration
+{
+ ///
+ /// Provides access to the underlying configuration. This is useful for extensions.
+ ///
+ new IMediatorDescriptorExtension Extend();
+
+ ///
+ /// Provides access to the underlying configuration. This is useful for extensions.
+ ///
+ IMediatorDescriptorExtension ExtendWith(Action> configure);
+
+ ///
+ /// Provides access to the underlying configuration. This is useful for extensions.
+ ///
+ IMediatorDescriptorExtension ExtendWith(
+ Action, TState> configure,
+ TState state);
+}
+
+///
+/// An untyped mediator descriptor that provides access to extension points for the underlying configuration.
+///
+public interface IMediatorDescriptor
+{
+ ///
+ /// Provides access to the underlying configuration. This is useful for extensions.
+ ///
+ IMediatorDescriptorExtension Extend();
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorDescriptorExtension.cs b/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorDescriptorExtension.cs
new file mode 100644
index 00000000000..b75e5491bbd
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorDescriptorExtension.cs
@@ -0,0 +1,26 @@
+namespace Mocha.Mediator;
+
+///
+/// Provides typed access to the underlying configuration of a descriptor for use by extensions.
+///
+/// The configuration type.
+public interface IMediatorDescriptorExtension : IMediatorDescriptorExtension where T : MediatorConfiguration
+{
+ ///
+ /// The type definition.
+ ///
+ new T Configuration { get; }
+
+ MediatorConfiguration IMediatorDescriptorExtension.Configuration => Configuration;
+}
+
+///
+/// Provides untyped access to the underlying configuration and context of a descriptor for use by extensions.
+///
+public interface IMediatorDescriptorExtension : IHasMediatorConfigurationContext
+{
+ ///
+ /// The type definition.
+ ///
+ MediatorConfiguration Configuration { get; }
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorHandlerDescriptor.cs b/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorHandlerDescriptor.cs
new file mode 100644
index 00000000000..be9ddadf816
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Descriptors/IMediatorHandlerDescriptor.cs
@@ -0,0 +1,6 @@
+namespace Mocha.Mediator;
+
+///
+/// Describes the configuration surface for a mediator handler.
+///
+public interface IMediatorHandlerDescriptor : IMediatorDescriptor;
diff --git a/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorConfigurationContext.cs b/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorConfigurationContext.cs
new file mode 100644
index 00000000000..52f99657628
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorConfigurationContext.cs
@@ -0,0 +1,13 @@
+namespace Mocha.Mediator;
+
+internal sealed class MediatorConfigurationContext(
+ MediatorOptions options,
+ IServiceProvider services,
+ IFeatureCollection features) : IMediatorConfigurationContext
+{
+ public IServiceProvider Services => services;
+
+ public MediatorOptions Options => options;
+
+ public IFeatureCollection Features => features;
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorDescriptorBase.cs b/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorDescriptorBase.cs
new file mode 100644
index 00000000000..4034966488d
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorDescriptorBase.cs
@@ -0,0 +1,41 @@
+namespace Mocha.Mediator;
+
+///
+/// Base class for mediator descriptors that provides extension point access and the configuration context.
+///
+/// The configuration type this descriptor manages.
+public abstract class MediatorDescriptorBase(IMediatorConfigurationContext context)
+ : IMediatorDescriptor
+ , IMediatorDescriptorExtension where T : MediatorConfiguration
+{
+ protected internal IMediatorConfigurationContext Context { get; } =
+ context ?? throw new ArgumentNullException(nameof(context));
+
+ IMediatorConfigurationContext IHasMediatorConfigurationContext.Context => Context;
+
+ protected internal abstract T Configuration { get; protected set; }
+
+ T IMediatorDescriptorExtension.Configuration => Configuration;
+
+ public IMediatorDescriptorExtension Extend() => this;
+
+ IMediatorDescriptorExtension IMediatorDescriptor.Extend() => Extend();
+
+ public IMediatorDescriptorExtension ExtendWith(Action> configure)
+ {
+ ArgumentNullException.ThrowIfNull(configure);
+
+ configure(this);
+ return this;
+ }
+
+ public IMediatorDescriptorExtension ExtendWith(
+ Action, TState> configure,
+ TState state)
+ {
+ ArgumentNullException.ThrowIfNull(configure);
+
+ configure(this, state);
+ return this;
+ }
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorHandlerDescriptor.cs b/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorHandlerDescriptor.cs
new file mode 100644
index 00000000000..1706e688ff4
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/Descriptors/MediatorHandlerDescriptor.cs
@@ -0,0 +1,112 @@
+namespace Mocha.Mediator;
+
+///
+/// Provides a fluent API for configuring a mediator handler during setup.
+/// Auto-detects handler kind by inspecting the handler type's interfaces.
+///
+public class MediatorHandlerDescriptor
+ : MediatorDescriptorBase
+ , IMediatorHandlerDescriptor
+{
+ ///
+ /// Creates a new handler descriptor that auto-detects handler metadata
+ /// from the specified handler type's interfaces.
+ ///
+ /// The mediator configuration context.
+ /// The concrete handler implementation type.
+ public MediatorHandlerDescriptor(IMediatorConfigurationContext context, Type handlerType)
+ : base(context)
+ {
+ ArgumentNullException.ThrowIfNull(handlerType);
+
+ Configuration = new MediatorHandlerConfiguration();
+ Configuration.HandlerType = handlerType;
+ }
+
+ protected internal override MediatorHandlerConfiguration Configuration { get; protected set; }
+
+ ///
+ /// Builds and returns the finalized from this
+ /// descriptor's accumulated settings. If no configuration was applied via
+ /// , auto-detects handler metadata
+ /// from the handler type's interfaces.
+ ///
+ public MediatorHandlerConfiguration CreateConfiguration()
+ {
+ if (Configuration.MessageType is null)
+ {
+ DetectHandler(Configuration.HandlerType!);
+ }
+
+ return Configuration;
+ }
+
+ private void DetectHandler(Type handlerType)
+ {
+ var found = false;
+
+ foreach (var @interface in handlerType.GetInterfaces())
+ {
+ if (!@interface.IsGenericType)
+ {
+ continue;
+ }
+
+ var genericDef = @interface.GetGenericTypeDefinition();
+
+ if (genericDef == typeof(ICommandHandler<,>))
+ {
+ if (found)
+ {
+ throw ThrowHelper.MultipleHandlerInterfaces(handlerType);
+ }
+
+ var args = @interface.GetGenericArguments();
+ Configuration.MessageType = args[0];
+ Configuration.ResponseType = args[1];
+ Configuration.Kind = MediatorHandlerKind.CommandResponse;
+ found = true;
+ }
+ else if (genericDef == typeof(ICommandHandler<>))
+ {
+ if (found)
+ {
+ throw ThrowHelper.MultipleHandlerInterfaces(handlerType);
+ }
+
+ Configuration.MessageType = @interface.GetGenericArguments()[0];
+ Configuration.Kind = MediatorHandlerKind.Command;
+ found = true;
+ }
+ else if (genericDef == typeof(IQueryHandler<,>))
+ {
+ if (found)
+ {
+ throw ThrowHelper.MultipleHandlerInterfaces(handlerType);
+ }
+
+ var args = @interface.GetGenericArguments();
+ Configuration.MessageType = args[0];
+ Configuration.ResponseType = args[1];
+ Configuration.Kind = MediatorHandlerKind.Query;
+ found = true;
+ }
+ else if (genericDef == typeof(INotificationHandler<>))
+ {
+ if (found)
+ {
+ throw ThrowHelper.MultipleHandlerInterfaces(handlerType);
+ }
+
+ Configuration.MessageType = @interface.GetGenericArguments()[0];
+ Configuration.Kind = MediatorHandlerKind.Notification;
+ found = true;
+ }
+ }
+
+ if (!found)
+ {
+ throw ThrowHelper.HandlerInterfaceNotFound(handlerType);
+ }
+ }
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Mediator.cs b/src/Mocha/src/Mocha.Mediator/Mediator.cs
index 4296ac61539..83676d466ce 100644
--- a/src/Mocha/src/Mocha.Mediator/Mediator.cs
+++ b/src/Mocha/src/Mocha.Mediator/Mediator.cs
@@ -1,3 +1,4 @@
+using System.Collections.Immutable;
using System.Runtime.CompilerServices;
namespace Mocha.Mediator;
@@ -118,10 +119,41 @@ public ValueTask PublishAsync(
{
ArgumentNullException.ThrowIfNull(notification);
- var messageType = notification.GetType();
- var pipeline = runtime.GetPipeline(messageType);
- var context = runtime.RentContext();
+ return PublishCoreAsync(notification, notification.GetType(), cancellationToken);
+ }
+
+ ///
+ ValueTask IPublisher.PublishAsync(object notification, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(notification);
+
+ return PublishCoreAsync(notification, notification.GetType(), cancellationToken);
+ }
+
+ private ValueTask PublishCoreAsync(
+ object notification,
+ Type messageType,
+ CancellationToken cancellationToken)
+ {
+ var pipelines = runtime.GetNotificationPipelines(messageType);
+ if (pipelines.Length == 1)
+ {
+ return PublishSingle(pipelines[0], notification, messageType, cancellationToken);
+ }
+
+ return runtime.NotificationPublishMode == NotificationPublishMode.Concurrent
+ ? PublishConcurrently(pipelines, notification, messageType, cancellationToken)
+ : PublishSequentially(pipelines, notification, messageType, cancellationToken);
+ }
+
+ private ValueTask PublishSingle(
+ MediatorDelegate pipeline,
+ object notification,
+ Type messageType,
+ CancellationToken cancellationToken)
+ {
+ var context = runtime.RentContext();
context.Initialize(runtime, serviceProvider, notification, messageType, cancellationToken);
var task = pipeline(context);
@@ -129,22 +161,75 @@ public ValueTask PublishAsync(
if (task.IsCompletedSuccessfully)
{
runtime.ReturnContext(context);
-
return default;
}
return AwaitAndReturn(task, context);
}
- ///
- ValueTask IPublisher.PublishAsync(object notification, CancellationToken cancellationToken)
+ [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))]
+ private async ValueTask PublishSequentially(
+ ImmutableArray pipelines,
+ object notification,
+ Type messageType,
+ CancellationToken cancellationToken)
{
- ArgumentNullException.ThrowIfNull(notification);
+ for (var i = 0; i < pipelines.Length; i++)
+ {
+ var context = runtime.RentContext();
+ try
+ {
+ context.Initialize(runtime, serviceProvider, notification, messageType, cancellationToken);
+ await pipelines[i](context).ConfigureAwait(false);
+ }
+ finally
+ {
+ runtime.ReturnContext(context);
+ }
+ }
+ }
- var messageType = notification.GetType();
- var pipeline = runtime.GetPipeline(messageType);
- var context = runtime.RentContext();
+ [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))]
+ private async ValueTask PublishConcurrently(
+ ImmutableArray pipelines,
+ object notification,
+ Type messageType,
+ CancellationToken cancellationToken)
+ {
+ var count = pipelines.Length;
+ var tasks = new Task[count];
+
+ for (var i = 0; i < count; i++)
+ {
+ tasks[i] = RunPipeline(pipelines[i], notification, messageType, cancellationToken);
+ }
+
+ var whenAll = Task.WhenAll(tasks);
+ try
+ {
+ await whenAll.ConfigureAwait(false);
+ }
+ catch
+ {
+ // Task.WhenAll captures all exceptions, but await unwraps only the first.
+ // Re-throw the AggregateException to surface all failures.
+ if (whenAll.Exception is not null)
+ {
+ throw whenAll.Exception;
+ }
+
+ throw;
+ }
+ }
+
+ private Task RunPipeline(
+ MediatorDelegate pipeline,
+ object notification,
+ Type messageType,
+ CancellationToken cancellationToken)
+ {
+ var context = runtime.RentContext();
context.Initialize(runtime, serviceProvider, notification, messageType, cancellationToken);
var task = pipeline(context);
@@ -152,10 +237,10 @@ ValueTask IPublisher.PublishAsync(object notification, CancellationToken cancell
if (task.IsCompletedSuccessfully)
{
runtime.ReturnContext(context);
- return default;
+ return Task.CompletedTask;
}
- return AwaitAndReturn(task, context);
+ return AwaitAndReturn(task, context).AsTask();
}
[MethodImpl(MethodImplOptions.NoInlining)]
diff --git a/src/Mocha/src/Mocha.Mediator/MediatorOptions.cs b/src/Mocha/src/Mocha.Mediator/MediatorOptions.cs
index 77017811dbe..9a7cedfa0f8 100644
--- a/src/Mocha/src/Mocha.Mediator/MediatorOptions.cs
+++ b/src/Mocha/src/Mocha.Mediator/MediatorOptions.cs
@@ -12,4 +12,10 @@ public sealed class MediatorOptions
/// Default is .
///
public ServiceLifetime ServiceLifetime { get; set; } = ServiceLifetime.Scoped;
+
+ ///
+ /// Gets or sets how notification handler pipelines are dispatched.
+ /// Default is .
+ ///
+ public NotificationPublishMode NotificationPublishMode { get; set; } = NotificationPublishMode.Sequential;
}
diff --git a/src/Mocha/src/Mocha.Mediator/MediatorRuntime.cs b/src/Mocha/src/Mocha.Mediator/MediatorRuntime.cs
index d63da72c930..51966cab27b 100644
--- a/src/Mocha/src/Mocha.Mediator/MediatorRuntime.cs
+++ b/src/Mocha/src/Mocha.Mediator/MediatorRuntime.cs
@@ -1,4 +1,5 @@
using System.Collections.Frozen;
+using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.ObjectPool;
@@ -11,6 +12,7 @@ namespace Mocha.Mediator;
public sealed class MediatorRuntime : IMediatorRuntime
{
private readonly FrozenDictionary _pipelines;
+ private readonly FrozenDictionary> _notificationPipelines;
private readonly ObjectPool _contextPool;
[ThreadStatic]
@@ -18,12 +20,16 @@ public sealed class MediatorRuntime : IMediatorRuntime
internal MediatorRuntime(
FrozenDictionary pipelines,
+ FrozenDictionary> notificationPipelines,
IMediatorPools pools,
- IFeatureCollection features)
+ IFeatureCollection features,
+ NotificationPublishMode notificationPublishMode)
{
_pipelines = pipelines;
+ _notificationPipelines = notificationPipelines;
_contextPool = pools.MediatorContext;
Features = features;
+ NotificationPublishMode = notificationPublishMode;
}
///
@@ -31,6 +37,11 @@ internal MediatorRuntime(
///
public IFeatureCollection Features { get; }
+ ///
+ /// Gets the notification publish mode for this mediator runtime.
+ ///
+ internal NotificationPublishMode NotificationPublishMode { get; }
+
///
/// Gets the compiled pipeline delegate for the specified message type.
///
@@ -42,7 +53,7 @@ public MediatorDelegate GetPipeline(Type messageType)
return pipeline;
}
- return ThrowMissingPipeline(messageType);
+ throw ThrowHelper.MissingPipeline(messageType);
}
///
@@ -80,8 +91,17 @@ public void ReturnContext(MediatorContext context)
}
}
- [MethodImpl(MethodImplOptions.NoInlining)]
- private static MediatorDelegate ThrowMissingPipeline(Type messageType)
- => throw new InvalidOperationException(
- $"No pipeline registered for message type {messageType}");
+ ///
+ /// Gets the compiled notification pipeline delegates for the specified notification type.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ImmutableArray GetNotificationPipelines(Type notificationType)
+ {
+ if (_notificationPipelines.TryGetValue(notificationType, out var pipelines))
+ {
+ return pipelines;
+ }
+
+ throw ThrowHelper.MissingNotificationPipeline(notificationType);
+ }
}
diff --git a/src/Mocha/src/Mocha.Mediator/NotificationPublishMode.cs b/src/Mocha/src/Mocha.Mediator/NotificationPublishMode.cs
new file mode 100644
index 00000000000..0f95bde5f32
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/NotificationPublishMode.cs
@@ -0,0 +1,24 @@
+namespace Mocha.Mediator;
+
+///
+/// Specifies how notification handler pipelines are dispatched.
+///
+public enum NotificationPublishMode
+{
+ ///
+ /// Handlers are invoked sequentially, awaiting each before proceeding to the next.
+ /// If a handler throws, subsequent handlers are not invoked.
+ ///
+ Sequential,
+
+ ///
+ /// Handlers are invoked concurrently using .
+ /// All handlers are started simultaneously and awaited together.
+ ///
+ /// Warning: All concurrent handler pipelines share the same scoped
+ /// . Scoped services such as DbContext
+ /// are not thread-safe and must not be used concurrently across handlers.
+ ///
+ ///
+ Concurrent
+}
diff --git a/src/Mocha/src/Mocha.Mediator/Pipeline/ForeachAwaitPublisher.cs b/src/Mocha/src/Mocha.Mediator/Pipeline/ForeachAwaitPublisher.cs
deleted file mode 100644
index 5e90357a51a..00000000000
--- a/src/Mocha/src/Mocha.Mediator/Pipeline/ForeachAwaitPublisher.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-namespace Mocha.Mediator;
-
-///
-/// Represents a notification publishing strategy that dispatches to each handler sequentially,
-/// awaiting completion before proceeding to the next.
-///
-public sealed class ForeachAwaitPublisher : INotificationStrategy
-{
- ///
- /// Publishes a notification to each handler in sequence, awaiting each one before proceeding.
- ///
- /// The type of notification to publish.
- /// The collection of handlers to notify.
- /// The notification instance to publish.
- /// A token to observe for cancellation requests.
- /// A representing the asynchronous operation.
- public ValueTask PublishAsync(
- IReadOnlyList> handlers,
- TNotification notification,
- CancellationToken cancellationToken)
- where TNotification : INotification
- {
- var count = handlers.Count;
-
- if (count == 0)
- {
- return default;
- }
-
- if (count == 1)
- {
- return handlers[0].HandleAsync(notification, cancellationToken);
- }
-
- return PublishSequentially(handlers, notification, cancellationToken, count);
- }
-
- private static async ValueTask PublishSequentially(
- IReadOnlyList> handlers,
- TNotification notification,
- CancellationToken cancellationToken,
- int count)
- where TNotification : INotification
- {
- for (var i = 0; i < count; i++)
- {
- await handlers[i].HandleAsync(notification, cancellationToken).ConfigureAwait(false);
- }
- }
-}
diff --git a/src/Mocha/src/Mocha.Mediator/Pipeline/MediatorPipelineConfiguration.cs b/src/Mocha/src/Mocha.Mediator/Pipeline/MediatorPipelineConfiguration.cs
deleted file mode 100644
index 351504660f7..00000000000
--- a/src/Mocha/src/Mocha.Mediator/Pipeline/MediatorPipelineConfiguration.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.ComponentModel;
-
-namespace Mocha.Mediator;
-
-///
-/// Describes a pipeline to be compiled for a specific message type.
-/// Carries all metadata needed by the middleware compiler so it does not
-/// have to derive information from the message type at runtime.
-/// This type is intended for use by source-generated code.
-///
-[EditorBrowsable(EditorBrowsableState.Never)]
-public sealed class MediatorPipelineConfiguration
-{
- ///
- /// Gets the message type this pipeline handles.
- ///
- public required Type MessageType { get; init; }
-
- ///
- /// Gets the response type produced by the handler,
- /// or for void commands and notifications.
- /// For stream handlers this is IAsyncEnumerable<TResponse>.
- ///
- public Type? ResponseType { get; init; }
-
- ///
- /// Gets the terminal delegate that invokes the handler.
- /// This is the innermost layer of the middleware pipeline.
- ///
- public required MediatorDelegate Terminal { get; init; }
-}
diff --git a/src/Mocha/src/Mocha.Mediator/Pipeline/NotificationStrategyFeature.cs b/src/Mocha/src/Mocha.Mediator/Pipeline/NotificationStrategyFeature.cs
deleted file mode 100644
index 3eebc024c58..00000000000
--- a/src/Mocha/src/Mocha.Mediator/Pipeline/NotificationStrategyFeature.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Mocha.Mediator;
-
-///
-/// Feature that carries the resolved on the mediator runtime.
-///
-internal sealed class NotificationStrategyFeature(INotificationStrategy strategy)
-{
- public INotificationStrategy Strategy { get; } = strategy;
-}
diff --git a/src/Mocha/src/Mocha.Mediator/Pipeline/PipelineBuilder.cs b/src/Mocha/src/Mocha.Mediator/Pipeline/PipelineBuilder.cs
index 243e2568ee2..e49a8a859e2 100644
--- a/src/Mocha/src/Mocha.Mediator/Pipeline/PipelineBuilder.cs
+++ b/src/Mocha/src/Mocha.Mediator/Pipeline/PipelineBuilder.cs
@@ -6,7 +6,7 @@
namespace Mocha.Mediator;
///
-/// Provides terminal delegate factories for each handler kind.
+/// Provides pipeline delegate factories for each handler kind.
/// These delegates form the innermost layer of the middleware pipeline,
/// resolving handlers from the scoped service provider and invoking them.
/// This class is intended for use by source-generated code.
@@ -15,16 +15,15 @@ namespace Mocha.Mediator;
public static class PipelineBuilder
{
///
- /// Builds a terminal delegate for a void command handler.
+ /// Builds a pipeline delegate for a void command handler.
///
- public static MediatorDelegate BuildVoidCommandTerminal()
+ public static MediatorDelegate BuildCommandPipeline()
+ where THandler : class, ICommandHandler
where TCommand : ICommand
{
- var serviceType = typeof(ICommandHandler);
-
- return ctx =>
+ return static ctx =>
{
- var handler = (ICommandHandler)ctx.Services.GetRequiredService(serviceType);
+ var handler = ctx.Services.GetRequiredService();
var task = handler.HandleAsync((TCommand)ctx.Message, ctx.CancellationToken);
if (task.IsCompletedSuccessfully)
@@ -44,16 +43,15 @@ static async ValueTask Awaited(ValueTask t)
}
///
- /// Builds a terminal delegate for a command handler that returns a response.
+ /// Builds a pipeline delegate for a command handler that returns a response.
///
- public static MediatorDelegate BuildCommandTerminal()
+ public static MediatorDelegate BuildCommandResponsePipeline()
+ where THandler : class, ICommandHandler
where TCommand : ICommand
{
- var serviceType = typeof(ICommandHandler);
-
- return ctx =>
+ return static ctx =>
{
- var handler = (ICommandHandler)ctx.Services.GetRequiredService(serviceType);
+ var handler = ctx.Services.GetRequiredService();
var task = handler.HandleAsync((TCommand)ctx.Message, ctx.CancellationToken);
if (task.IsCompletedSuccessfully)
@@ -74,16 +72,15 @@ static async ValueTask Awaited(ValueTask t, IMediatorContext c)
}
///
- /// Builds a terminal delegate for a query handler.
+ /// Builds a pipeline delegate for a query handler.
///
- public static MediatorDelegate BuildQueryTerminal()
+ public static MediatorDelegate BuildQueryPipeline()
+ where THandler : class, IQueryHandler
where TQuery : IQuery
{
- var serviceType = typeof(IQueryHandler);
-
- return ctx =>
+ return static ctx =>
{
- var handler = (IQueryHandler)ctx.Services.GetRequiredService(serviceType);
+ var handler = ctx.Services.GetRequiredService();
var task = handler.HandleAsync((TQuery)ctx.Message, ctx.CancellationToken);
if (task.IsCompletedSuccessfully)
@@ -104,47 +101,16 @@ static async ValueTask Awaited(ValueTask t, IMediatorContext c)
}
///
- /// Builds a terminal delegate for a notification with handler types known at compile time.
+ /// Builds a pipeline delegate for a single notification handler.
///
- public static MediatorDelegate BuildNotificationTerminal(Type[] handlerTypes)
+ public static MediatorDelegate BuildNotificationPipeline()
+ where THandler : class, INotificationHandler
where TNotification : INotification
{
- if (handlerTypes.Length == 1)
- {
- var handlerType = handlerTypes[0];
- return ctx =>
- {
- var handler = (INotificationHandler)ctx.Services.GetRequiredService(handlerType);
- var task = handler.HandleAsync((TNotification)ctx.Message, ctx.CancellationToken);
-
- if (task.IsCompletedSuccessfully)
- {
- return default;
- }
-
- return Awaited(task);
-
- [MethodImpl(MethodImplOptions.NoInlining)]
- [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))]
- static async ValueTask Awaited(ValueTask t)
- {
- await t.ConfigureAwait(false);
- }
- };
- }
-
- return ctx =>
+ return static ctx =>
{
- var strategy = ctx.Runtime.Features
- .GetRequired().Strategy;
-
- var handlers = new INotificationHandler[handlerTypes.Length];
- for (var i = 0; i < handlerTypes.Length; i++)
- {
- handlers[i] = (INotificationHandler)ctx.Services.GetRequiredService(handlerTypes[i]);
- }
-
- var task = strategy.PublishAsync(handlers, (TNotification)ctx.Message, ctx.CancellationToken);
+ var handler = ctx.Services.GetRequiredService();
+ var task = handler.HandleAsync((TNotification)ctx.Message, ctx.CancellationToken);
if (task.IsCompletedSuccessfully)
{
diff --git a/src/Mocha/src/Mocha.Mediator/Pipeline/TaskWhenAllPublisher.cs b/src/Mocha/src/Mocha.Mediator/Pipeline/TaskWhenAllPublisher.cs
deleted file mode 100644
index df3dd75001c..00000000000
--- a/src/Mocha/src/Mocha.Mediator/Pipeline/TaskWhenAllPublisher.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-namespace Mocha.Mediator;
-
-///
-/// Represents a notification publishing strategy that dispatches to all handlers concurrently
-/// using .
-///
-public sealed class TaskWhenAllPublisher : INotificationStrategy
-{
- ///
- /// Publishes a notification to all handlers concurrently, awaiting all completions.
- ///
- /// The type of notification to publish.
- /// The collection of handlers to notify.
- /// The notification instance to publish.
- /// A token to observe for cancellation requests.
- /// A representing the asynchronous operation.
- public ValueTask PublishAsync(
- IReadOnlyList> handlers,
- TNotification notification,
- CancellationToken cancellationToken)
- where TNotification : INotification
- {
- var count = handlers.Count;
-
- if (count == 0)
- {
- return default;
- }
-
- if (count == 1)
- {
- return handlers[0].HandleAsync(notification, cancellationToken);
- }
-
- return PublishConcurrently(handlers, notification, cancellationToken, count);
- }
-
- private static async ValueTask PublishConcurrently(
- IReadOnlyList> handlers,
- TNotification notification,
- CancellationToken cancellationToken,
- int count)
- where TNotification : INotification
- {
- var tasks = new Task[count];
-
- for (var i = 0; i < count; i++)
- {
- tasks[i] = handlers[i].HandleAsync(notification, cancellationToken).AsTask();
- }
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
-}
diff --git a/src/Mocha/src/Mocha.Mediator/ThrowHelper.cs b/src/Mocha/src/Mocha.Mediator/ThrowHelper.cs
new file mode 100644
index 00000000000..0c6af56845c
--- /dev/null
+++ b/src/Mocha/src/Mocha.Mediator/ThrowHelper.cs
@@ -0,0 +1,34 @@
+namespace Mocha.Mediator;
+
+internal static class ThrowHelper
+{
+ 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 BeforeAndAfterConflict()
+ => new ArgumentException(
+ "Only one of 'before' or 'after' can be specified at the same time.");
+
+ public static Exception MiddlewareKeyNotFound(string key)
+ => new InvalidOperationException(
+ $"The middleware with the key `{key}` was not found.");
+
+ public static Exception UnknownHandlerKind(MediatorHandlerKind kind)
+ => new InvalidOperationException(
+ $"Unknown handler kind: {kind}");
+
+ public static Exception HandlerInterfaceNotFound(Type handlerType)
+ => new InvalidOperationException(
+ $"Type '{handlerType}' does not implement any known mediator handler interface.");
+
+ public static Exception MultipleHandlerInterfaces(Type handlerType)
+ => new InvalidOperationException(
+ $"Type '{handlerType}' implements multiple mediator handler interfaces. "
+ + "A handler must implement exactly one of ICommandHandler<>, IQueryHandler<,>, or INotificationHandler<>.");
+}
diff --git a/src/Mocha/src/Mocha/Builder/MessageBusBuilder.Middlewares.cs b/src/Mocha/src/Mocha/Builder/MessageBusBuilder.Middlewares.cs
index d7db6eb8865..8d2ebbaa1f9 100644
--- a/src/Mocha/src/Mocha/Builder/MessageBusBuilder.Middlewares.cs
+++ b/src/Mocha/src/Mocha/Builder/MessageBusBuilder.Middlewares.cs
@@ -39,8 +39,7 @@ public IMessageBusBuilder UseConsume(
{
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
@@ -90,8 +89,7 @@ public IMessageBusBuilder UseReceive(
{
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
@@ -141,8 +139,7 @@ public IMessageBusBuilder UseDispatch(
{
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
diff --git a/src/Mocha/src/Mocha/Builder/MessageBusBuilder.cs b/src/Mocha/src/Mocha/Builder/MessageBusBuilder.cs
index ad824da1cde..bb1949a5982 100644
--- a/src/Mocha/src/Mocha/Builder/MessageBusBuilder.cs
+++ b/src/Mocha/src/Mocha/Builder/MessageBusBuilder.cs
@@ -120,15 +120,14 @@ public IMessageBusBuilder AddHandler(Action confi
}
else
{
- throw new InvalidOperationException(
- "Handler type must be either an event handler, a request handler, or both.");
+ throw ThrowHelper.InvalidHandlerType();
}
var consumer = Activator.CreateInstance(consumerType, configure) as Consumer;
if (consumer is null)
{
- throw new InvalidOperationException($"Failed to create consumer for type {consumerType}");
+ throw ThrowHelper.FailedToCreateConsumer(consumerType);
}
_consumers.Add(consumer);
diff --git a/src/Mocha/src/Mocha/Configuration/RootServiceProviderAccessorExtensions.cs b/src/Mocha/src/Mocha/Configuration/RootServiceProviderAccessorExtensions.cs
index 7baa19db671..62a768213f0 100644
--- a/src/Mocha/src/Mocha/Configuration/RootServiceProviderAccessorExtensions.cs
+++ b/src/Mocha/src/Mocha/Configuration/RootServiceProviderAccessorExtensions.cs
@@ -20,6 +20,6 @@ public static class RootServiceProviderAccessorExtensions
public static IServiceProvider GetApplicationServices(this IServiceProvider sp)
{
return sp.GetService()?.ServiceProvider
- ?? throw new InvalidOperationException("No root services found");
+ ?? throw ThrowHelper.NoRootServicesFound();
}
}
diff --git a/src/Mocha/src/Mocha/Consumers/Consumer.cs b/src/Mocha/src/Mocha/Consumers/Consumer.cs
index 4bbd74bda87..9afc99cec40 100644
--- a/src/Mocha/src/Mocha/Consumers/Consumer.cs
+++ b/src/Mocha/src/Mocha/Consumers/Consumer.cs
@@ -88,7 +88,7 @@ public async ValueTask ProcessAsync(IReceiveContext context)
{
if (context is not IConsumeContext handlerContext)
{
- throw new InvalidOperationException("Context is not a handler context");
+ throw ThrowHelper.InvalidHandlerContext();
}
await _pipeline(handlerContext);
@@ -123,11 +123,11 @@ internal void Initialize(IMessagingSetupContext context)
if (Configuration is null)
{
- throw new InvalidOperationException("Handler configuration is null");
+ throw ThrowHelper.HandlerConfigurationMissing();
}
// TODO should we assign a default name in the Action? GetType().Name?
- Name = Configuration.Name ?? throw new InvalidOperationException("Consumer name is null");
+ Name = Configuration.Name ?? throw ThrowHelper.ConsumerNameRequired();
Identity ??= GetType();
foreach (var route in Configuration!.Routes)
@@ -189,7 +189,7 @@ private void AssertUninitialized()
{
if (_isInitialized)
{
- throw new InvalidOperationException("Handler already initialized");
+ throw ThrowHelper.HandlerAlreadyInitialized();
}
}
diff --git a/src/Mocha/src/Mocha/Consumers/Descriptors/ConsumerDescriptor.cs b/src/Mocha/src/Mocha/Consumers/Descriptors/ConsumerDescriptor.cs
index 4bbba6566e8..eaec0e93f81 100644
--- a/src/Mocha/src/Mocha/Consumers/Descriptors/ConsumerDescriptor.cs
+++ b/src/Mocha/src/Mocha/Consumers/Descriptors/ConsumerDescriptor.cs
@@ -45,8 +45,7 @@ public IConsumerDescriptor UseConsumer(
{
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
diff --git a/src/Mocha/src/Mocha/Consumers/Implementations/ReplyConsumer.cs b/src/Mocha/src/Mocha/Consumers/Implementations/ReplyConsumer.cs
index a3868bc002b..0bef89c71c8 100644
--- a/src/Mocha/src/Mocha/Consumers/Implementations/ReplyConsumer.cs
+++ b/src/Mocha/src/Mocha/Consumers/Implementations/ReplyConsumer.cs
@@ -39,7 +39,7 @@ protected override ValueTask ConsumeAsync(IConsumeContext context)
if (message is null)
{
- throw new InvalidOperationException("Response body is not set. Could not be parsed.");
+ throw ThrowHelper.ResponseBodyNotSet();
}
if (message is NotAcknowledgedEvent failure)
@@ -56,7 +56,7 @@ protected override ValueTask ConsumeAsync(IConsumeContext context)
else if (!responseManager.CompletePromise(context.CorrelationId, message))
{
// A late/unknown reply indicates there is no active waiter for this correlation id.
- throw new InvalidOperationException("Promise with correlation ID not found.");
+ throw ThrowHelper.PromiseNotFound();
}
}
catch (Exception ex)
diff --git a/src/Mocha/src/Mocha/Consumers/Implementations/RequestConsumer.cs b/src/Mocha/src/Mocha/Consumers/Implementations/RequestConsumer.cs
index f3242a7bfea..68484751a55 100644
--- a/src/Mocha/src/Mocha/Consumers/Implementations/RequestConsumer.cs
+++ b/src/Mocha/src/Mocha/Consumers/Implementations/RequestConsumer.cs
@@ -51,7 +51,7 @@ protected override async ValueTask ConsumeAsync(IConsumeContext context)
// Request contracts require a response message; null would break caller expectations.
if (response is null)
{
- throw new InvalidOperationException("Response is null.");
+ throw ThrowHelper.ResponseIsNull();
}
// Copy request metadata (correlation/saga-related headers) onto the reply path.
diff --git a/src/Mocha/src/Mocha/Context/ConsumeContext~1.cs b/src/Mocha/src/Mocha/Context/ConsumeContext~1.cs
index 85a351c7547..8549247f7b8 100644
--- a/src/Mocha/src/Mocha/Context/ConsumeContext~1.cs
+++ b/src/Mocha/src/Mocha/Context/ConsumeContext~1.cs
@@ -15,7 +15,7 @@ public ConsumeContext(IConsumeContext inner)
public TMessage Message
=> field ??=
- Inner.GetMessage() ?? throw new InvalidOperationException("Could not deserialize message");
+ Inner.GetMessage() ?? throw ThrowHelper.CouldNotDeserializeMessage();
public IFeatureCollection Features => Inner.Features;
diff --git a/src/Mocha/src/Mocha/DeferredResponseManager.cs b/src/Mocha/src/Mocha/DeferredResponseManager.cs
index fbcebbe0cbd..e3fafebf3c9 100644
--- a/src/Mocha/src/Mocha/DeferredResponseManager.cs
+++ b/src/Mocha/src/Mocha/DeferredResponseManager.cs
@@ -50,7 +50,7 @@ public sealed class DeferredResponseManager(TimeProvider timeProvider)
return await promise.TaskCompletionSource.Task;
}
- throw new InvalidOperationException("Promise not found.");
+ throw ThrowHelper.PromiseNotFound();
}
///
diff --git a/src/Mocha/src/Mocha/Descriptions/MessageBusDescriptionBuilder.cs b/src/Mocha/src/Mocha/Descriptions/MessageBusDescriptionBuilder.cs
index be853e3e128..b0ab03152bc 100644
--- a/src/Mocha/src/Mocha/Descriptions/MessageBusDescriptionBuilder.cs
+++ b/src/Mocha/src/Mocha/Descriptions/MessageBusDescriptionBuilder.cs
@@ -34,7 +34,7 @@ public sealed class Context
internal MessageBusDescription ToDescription()
=> new(
- Host ?? throw new InvalidOperationException("Host description is missing."),
+ Host ?? throw ThrowHelper.HostDescriptionMissing(),
MessageTypes,
Consumers,
new RoutesDescription(InboundRoutes, OutboundRoutes),
diff --git a/src/Mocha/src/Mocha/Descriptors/IMessagingDescriptor.cs b/src/Mocha/src/Mocha/Descriptors/IMessagingDescriptor.cs
index 43c975dcec5..0bc93227492 100644
--- a/src/Mocha/src/Mocha/Descriptors/IMessagingDescriptor.cs
+++ b/src/Mocha/src/Mocha/Descriptors/IMessagingDescriptor.cs
@@ -9,17 +9,17 @@ public interface IMessagingDescriptor : IMessagingDescriptor where T : Me
///
/// Provides access to the underlying configuration. This is useful for extensions.
///
- new IDescriptorExtension Extend();
+ new IMessagingDescriptorExtension Extend();
///
/// Provides access to the underlying configuration. This is useful for extensions.
///
- IDescriptorExtension ExtendWith(Action> configure);
+ IMessagingDescriptorExtension ExtendWith(Action> configure);
///
/// Provides access to the underlying configuration. This is useful for extensions.
///
- IDescriptorExtension ExtendWith(Action, TState> configure, TState state);
+ IMessagingDescriptorExtension ExtendWith(Action, TState> configure, TState state);
}
///
@@ -30,5 +30,5 @@ public interface IMessagingDescriptor
///
/// Provides access to the underlying configuration. This is useful for extensions.
///
- IDescriptorExtension Extend();
+ IMessagingDescriptorExtension Extend();
}
diff --git a/src/Mocha/src/Mocha/Descriptors/IDescriptorExtension.cs b/src/Mocha/src/Mocha/Descriptors/IMessagingDescriptorExtension.cs
similarity index 66%
rename from src/Mocha/src/Mocha/Descriptors/IDescriptorExtension.cs
rename to src/Mocha/src/Mocha/Descriptors/IMessagingDescriptorExtension.cs
index 5dede61a3cb..60645c63714 100644
--- a/src/Mocha/src/Mocha/Descriptors/IDescriptorExtension.cs
+++ b/src/Mocha/src/Mocha/Descriptors/IMessagingDescriptorExtension.cs
@@ -4,20 +4,20 @@ namespace Mocha;
/// Provides typed access to the underlying configuration of a descriptor for use by extensions.
///
/// The configuration type.
-public interface IDescriptorExtension : IDescriptorExtension where T : MessagingConfiguration
+public interface IMessagingDescriptorExtension : IMessagingDescriptorExtension where T : MessagingConfiguration
{
///
/// The type definition.
///
new T Configuration { get; }
- MessagingConfiguration IDescriptorExtension.Configuration => Configuration;
+ MessagingConfiguration IMessagingDescriptorExtension.Configuration => Configuration;
}
///
/// Provides untyped access to the underlying configuration and context of a descriptor for use by extensions.
///
-public interface IDescriptorExtension : IHasConfigurationContext
+public interface IMessagingDescriptorExtension : IHasConfigurationContext
{
///
/// The type definition.
diff --git a/src/Mocha/src/Mocha/Descriptors/MessagingDescriptorBase.cs b/src/Mocha/src/Mocha/Descriptors/MessagingDescriptorBase.cs
index c52f41dac44..2512f4dd6e9 100644
--- a/src/Mocha/src/Mocha/Descriptors/MessagingDescriptorBase.cs
+++ b/src/Mocha/src/Mocha/Descriptors/MessagingDescriptorBase.cs
@@ -6,7 +6,7 @@ namespace Mocha;
/// The configuration type this descriptor manages.
public abstract class MessagingDescriptorBase(IMessagingConfigurationContext context)
: IMessagingDescriptor
- , IDescriptorExtension where T : MessagingConfiguration
+ , IMessagingDescriptorExtension where T : MessagingConfiguration
{
protected internal IMessagingConfigurationContext Context { get; } =
context ?? throw new ArgumentNullException(nameof(context));
@@ -15,13 +15,13 @@ public abstract class MessagingDescriptorBase(IMessagingConfigurationContext
protected internal abstract T Configuration { get; protected set; }
- T IDescriptorExtension.Configuration => Configuration;
+ T IMessagingDescriptorExtension.Configuration => Configuration;
- public IDescriptorExtension Extend() => this;
+ public IMessagingDescriptorExtension Extend() => this;
- IDescriptorExtension IMessagingDescriptor.Extend() => Extend();
+ IMessagingDescriptorExtension IMessagingDescriptor.Extend() => Extend();
- public IDescriptorExtension ExtendWith(Action> configure)
+ public IMessagingDescriptorExtension ExtendWith(Action> configure)
{
ArgumentNullException.ThrowIfNull(configure);
@@ -29,7 +29,7 @@ public IDescriptorExtension ExtendWith(Action> config
return this;
}
- public IDescriptorExtension ExtendWith(Action, TState> configure, TState state)
+ public IMessagingDescriptorExtension ExtendWith(Action, TState> configure, TState state)
{
ArgumentNullException.ThrowIfNull(configure);
diff --git a/src/Mocha/src/Mocha/Endpoints/Descriptors/DispatchEndpointDescriptor.cs b/src/Mocha/src/Mocha/Endpoints/Descriptors/DispatchEndpointDescriptor.cs
index 554fffd47f7..d3084bbffd7 100644
--- a/src/Mocha/src/Mocha/Endpoints/Descriptors/DispatchEndpointDescriptor.cs
+++ b/src/Mocha/src/Mocha/Endpoints/Descriptors/DispatchEndpointDescriptor.cs
@@ -29,8 +29,7 @@ public IDispatchEndpointDescriptor UseDispatch(
{
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
diff --git a/src/Mocha/src/Mocha/Endpoints/Descriptors/ReceiveEndpointDescriptor.cs b/src/Mocha/src/Mocha/Endpoints/Descriptors/ReceiveEndpointDescriptor.cs
index fcb113eb08c..16fb62c78d7 100644
--- a/src/Mocha/src/Mocha/Endpoints/Descriptors/ReceiveEndpointDescriptor.cs
+++ b/src/Mocha/src/Mocha/Endpoints/Descriptors/ReceiveEndpointDescriptor.cs
@@ -53,8 +53,7 @@ public IReceiveEndpointDescriptor UseReceive(
{
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
diff --git a/src/Mocha/src/Mocha/Endpoints/DispatchEndpoint.cs b/src/Mocha/src/Mocha/Endpoints/DispatchEndpoint.cs
index ba721e075b3..b0176c455c2 100644
--- a/src/Mocha/src/Mocha/Endpoints/DispatchEndpoint.cs
+++ b/src/Mocha/src/Mocha/Endpoints/DispatchEndpoint.cs
@@ -131,7 +131,7 @@ public void Initialize(IMessagingConfigurationContext context, DispatchEndpointC
Transport.Conventions.Configure(context, Transport, configuration);
Configuration = configuration;
Kind = configuration.Kind;
- Name = configuration.Name ?? throw new InvalidOperationException("Name is required");
+ Name = configuration.Name ?? throw ThrowHelper.EndpointNameRequired();
OnInitialize(context, configuration);
@@ -250,7 +250,7 @@ private void AssertUninitialized()
if (IsInitialized)
{
- throw new InvalidOperationException("Endpoint already initialized");
+ throw ThrowHelper.EndpointAlreadyInitialized();
}
}
diff --git a/src/Mocha/src/Mocha/Endpoints/EndpointRouter.cs b/src/Mocha/src/Mocha/Endpoints/EndpointRouter.cs
index e2e715f3bb2..58a3c699420 100644
--- a/src/Mocha/src/Mocha/Endpoints/EndpointRouter.cs
+++ b/src/Mocha/src/Mocha/Endpoints/EndpointRouter.cs
@@ -109,7 +109,7 @@ public DispatchEndpoint GetOrCreate(IMessagingConfigurationContext context, Uri
}
}
- throw new InvalidOperationException($"No transport can handle address: {address}");
+ throw ThrowHelper.NoTransportForAddress(address.ToString());
}
}
@@ -134,7 +134,7 @@ public void AddAddress(DispatchEndpoint endpoint, Uri address)
{
if (!_endpoints.TryGetValue(endpoint, out var addresses))
{
- throw new InvalidOperationException("Endpoint must be registered before adding addresses");
+ throw ThrowHelper.EndpointMustBeRegistered();
}
if (addresses.Contains(address))
diff --git a/src/Mocha/src/Mocha/Endpoints/ReceiveEndpoint.cs b/src/Mocha/src/Mocha/Endpoints/ReceiveEndpoint.cs
index 173f1a33812..686ba65f586 100644
--- a/src/Mocha/src/Mocha/Endpoints/ReceiveEndpoint.cs
+++ b/src/Mocha/src/Mocha/Endpoints/ReceiveEndpoint.cs
@@ -167,7 +167,7 @@ public void Initialize(IMessagingConfigurationContext context, ReceiveEndpointCo
Transport.Conventions.Configure(context, Transport, configuration);
Configuration = configuration;
Kind = configuration.Kind;
- Name = configuration.Name ?? throw new InvalidOperationException("Name is required");
+ Name = configuration.Name ?? throw ThrowHelper.EndpointNameRequired();
configuration.Features.CopyTo(Features);
OnInitialize(context, Configuration);
@@ -324,7 +324,7 @@ private void AssertUninitialized()
if (IsInitialized)
{
- throw new InvalidOperationException("Endpoint already initialized");
+ throw ThrowHelper.EndpointAlreadyInitialized();
}
}
diff --git a/src/Mocha/src/Mocha/Features/MessageFeatureContextExtensions.cs b/src/Mocha/src/Mocha/Features/MessageFeatureContextExtensions.cs
index ef983630f1b..384e35f1829 100644
--- a/src/Mocha/src/Mocha/Features/MessageFeatureContextExtensions.cs
+++ b/src/Mocha/src/Mocha/Features/MessageFeatureContextExtensions.cs
@@ -26,7 +26,7 @@ public static class MessageFeatureContextExtensions
if (context.Envelope is null)
{
- throw new InvalidOperationException("Envelope is required for deserialization");
+ throw ThrowHelper.EnvelopeRequired();
}
var serializer = context.GetSerializer();
@@ -56,7 +56,7 @@ public static class MessageFeatureContextExtensions
if (context.Envelope is null)
{
- throw new InvalidOperationException("Envelope is required for deserialization");
+ throw ThrowHelper.EnvelopeRequired();
}
var serializer = context.GetSerializer();
@@ -71,20 +71,19 @@ private static IMessageSerializer GetSerializer(this IMessageContext context)
{
if (context.MessageType is null)
{
- throw new InvalidOperationException("Message type is required for deserialization");
+ throw ThrowHelper.MessageTypeRequired();
}
if (context.ContentType is null)
{
- throw new InvalidOperationException("Content type is required for deserialization");
+ throw ThrowHelper.ContentTypeRequired();
}
var serializer = context.MessageType.GetSerializer(context.ContentType);
if (serializer is null)
{
- throw new InvalidOperationException(
- $"No serializer was found for message type {context.MessageType.Identity} and content type {context.ContentType}");
+ throw ThrowHelper.SerializerNotFound(context.MessageType.Identity, context.ContentType.Value);
}
return serializer;
diff --git a/src/Mocha/src/Mocha/MessageTypes/IMessageRouter.cs b/src/Mocha/src/Mocha/MessageTypes/IMessageRouter.cs
index e4b4c5745a4..7225168b1ba 100644
--- a/src/Mocha/src/Mocha/MessageTypes/IMessageRouter.cs
+++ b/src/Mocha/src/Mocha/MessageTypes/IMessageRouter.cs
@@ -180,7 +180,7 @@ public DispatchEndpoint GetEndpoint(
}
route.Complete(context);
- throw new InvalidOperationException($"No transport can handle message type: {messageType}");
+ throw ThrowHelper.NoTransportForMessageType(messageType);
}
}
@@ -190,7 +190,7 @@ public void AddOrUpdate(InboundRoute route)
if (!route.IsInitialized)
{
- throw new InvalidOperationException("Route must be initialized");
+ throw ThrowHelper.RouteMustBeInitialized();
}
lock (_lock)
@@ -240,7 +240,7 @@ public void AddOrUpdate(OutboundRoute route)
ArgumentNullException.ThrowIfNull(route);
if (!route.IsInitialized)
{
- throw new InvalidOperationException("Route must be initialized");
+ throw ThrowHelper.RouteMustBeInitialized();
}
lock (_lock)
diff --git a/src/Mocha/src/Mocha/MessageTypes/InboundRoute.cs b/src/Mocha/src/Mocha/MessageTypes/InboundRoute.cs
index 0dce018d2da..9c4d8154d6b 100644
--- a/src/Mocha/src/Mocha/MessageTypes/InboundRoute.cs
+++ b/src/Mocha/src/Mocha/MessageTypes/InboundRoute.cs
@@ -60,10 +60,10 @@ public void Initialize(IMessagingConfigurationContext context, InboundRouteConfi
}
else if (configuration.Kind != InboundRouteKind.Reply)
{
- throw new InvalidOperationException("Route requires a message type");
+ throw ThrowHelper.RouteRequiresMessageType();
}
- Consumer = configuration.Consumer ?? throw new InvalidOperationException("Route requires a consumer");
+ Consumer = configuration.Consumer ?? throw ThrowHelper.RouteRequiresConsumer();
Kind = configuration.Kind;
if (configuration.ResponseRuntimeType is not null)
@@ -99,7 +99,7 @@ public void Complete(IMessagingConfigurationContext context)
if (Endpoint is null)
{
- throw new InvalidOperationException("Endpoint is not connected");
+ throw ThrowHelper.RouteEndpointNotConnected();
}
MarkCompleted();
@@ -109,7 +109,7 @@ private void AssertNotInitialized()
{
if (IsInitialized)
{
- throw new InvalidOperationException("Route must not be initialized");
+ throw ThrowHelper.RouteMustNotBeInitialized();
}
}
@@ -117,7 +117,7 @@ private void AssertInitialized()
{
if (!IsInitialized)
{
- throw new InvalidOperationException("Rout must be initialized");
+ throw ThrowHelper.RouteMustBeInitialized();
}
}
@@ -125,7 +125,7 @@ private void AssertNotCompleted()
{
if (IsCompleted)
{
- throw new InvalidOperationException("Route must not be completed");
+ throw ThrowHelper.RouteMustNotBeCompleted();
}
}
diff --git a/src/Mocha/src/Mocha/MessageTypes/OutboundRoute.cs b/src/Mocha/src/Mocha/MessageTypes/OutboundRoute.cs
index 4df49170a95..ca39b990319 100644
--- a/src/Mocha/src/Mocha/MessageTypes/OutboundRoute.cs
+++ b/src/Mocha/src/Mocha/MessageTypes/OutboundRoute.cs
@@ -58,7 +58,7 @@ public void Initialize(IMessagingConfigurationContext context, OutboundRouteConf
}
else
{
- throw new InvalidOperationException("Cannot initialize outbound route without a message type");
+ throw ThrowHelper.RouteRequiresMessageType();
}
Destination = configuration.Destination;
@@ -92,7 +92,7 @@ public void Complete(IMessagingConfigurationContext context)
if (Endpoint is null)
{
- throw new InvalidOperationException("Endpoint is not connected");
+ throw ThrowHelper.RouteEndpointNotConnected();
}
context.Router.AddOrUpdate(this);
@@ -104,7 +104,7 @@ private void AssertNotInitialized()
{
if (IsInitialized)
{
- throw new InvalidOperationException("Route must not be initialized");
+ throw ThrowHelper.RouteMustNotBeInitialized();
}
}
@@ -112,7 +112,7 @@ private void AssertInitialized()
{
if (!IsInitialized)
{
- throw new InvalidOperationException("Route must be initialized");
+ throw ThrowHelper.RouteMustBeInitialized();
}
}
@@ -120,7 +120,7 @@ private void AssertNotCompleted()
{
if (IsCompleted)
{
- throw new InvalidOperationException("Route must not be completed");
+ throw ThrowHelper.RouteMustNotBeCompleted();
}
}
diff --git a/src/Mocha/src/Mocha/Middlewares/DefaultMessageBus.cs b/src/Mocha/src/Mocha/Middlewares/DefaultMessageBus.cs
index f66adea7802..b4e72a62cff 100644
--- a/src/Mocha/src/Mocha/Middlewares/DefaultMessageBus.cs
+++ b/src/Mocha/src/Mocha/Middlewares/DefaultMessageBus.cs
@@ -206,14 +206,13 @@ public async ValueTask ReplyAsync(
var transport = runtime.GetTransport(options.ReplyAddress);
if (transport is null)
{
- throw new InvalidOperationException($"Transport not found for address {options.ReplyAddress}");
+ throw ThrowHelper.TransportNotFoundForAddress(options.ReplyAddress.ToString());
}
var replyEndpoint = transport.ReplyDispatchEndpoint;
if (replyEndpoint is null)
{
- throw new InvalidOperationException(
- $"Reply dispatch endpoint not found for address {options.ReplyAddress}");
+ throw ThrowHelper.ReplyDispatchEndpointNotFound(options.ReplyAddress.ToString());
}
// var operationName = "reply";
@@ -293,7 +292,7 @@ private async ValueTask RequestAndWaitAsync(
return response;
}
- throw new InvalidOperationException("Unexpected response type.");
+ throw ThrowHelper.UnexpectedResponseType();
}
private void PropagateCorrelationIds(DispatchContext context)
diff --git a/src/Mocha/src/Mocha/Middlewares/Dispatch/DispatchSerializerMiddleware.cs b/src/Mocha/src/Mocha/Middlewares/Dispatch/DispatchSerializerMiddleware.cs
index b123fdad145..c2de9906a70 100644
--- a/src/Mocha/src/Mocha/Middlewares/Dispatch/DispatchSerializerMiddleware.cs
+++ b/src/Mocha/src/Mocha/Middlewares/Dispatch/DispatchSerializerMiddleware.cs
@@ -20,28 +20,24 @@ public async ValueTask InvokeAsync(IDispatchContext context, DispatchDelegate ne
{
if (context.Message is null)
{
- throw new InvalidOperationException(
- "To send a message either the body must be set or the message must be set");
+ throw ThrowHelper.DispatchMessageRequired();
}
if (context.MessageType is null)
{
- throw new InvalidOperationException(
- "To send a message a message type must be set. Otherwise there is no way to serialize the message");
+ throw ThrowHelper.DispatchMessageTypeRequired();
}
if (context.ContentType is null)
{
- throw new InvalidOperationException(
- "To send a message a content type must be set. Otherwise there is no way to serialize the message");
+ throw ThrowHelper.DispatchContentTypeRequired();
}
var serializer = context.MessageType.GetSerializer(context.ContentType);
if (serializer is null)
{
- throw new InvalidOperationException(
- $"No serializer found for content type {context.ContentType.Value} and message type {context.MessageType.Identity}");
+ throw ThrowHelper.DispatchSerializerNotFound(context.ContentType.Value, context.MessageType.Identity);
}
serializer.Serialize(context.Message, context.Body);
diff --git a/src/Mocha/src/Mocha/Middlewares/Extensions/MiddlewareConfigurationExtensions.cs b/src/Mocha/src/Mocha/Middlewares/Extensions/MiddlewareConfigurationExtensions.cs
index ab7dadfa13d..d9f0f1d3bff 100644
--- a/src/Mocha/src/Mocha/Middlewares/Extensions/MiddlewareConfigurationExtensions.cs
+++ b/src/Mocha/src/Mocha/Middlewares/Extensions/MiddlewareConfigurationExtensions.cs
@@ -29,7 +29,7 @@ public static void Append(
var index = pipeline.FindIndex(m => m.Key == after);
if (index == -1)
{
- throw new InvalidOperationException($"Middleware with key {after} not found");
+ throw ThrowHelper.MiddlewareKeyNotFound(after);
}
pipeline.Insert(index + 1, configuration);
@@ -58,7 +58,7 @@ public static void Prepend(
var index = pipeline.FindIndex(m => m.Key == before);
if (index == -1)
{
- throw new InvalidOperationException($"Middleware with key {before} not found");
+ throw ThrowHelper.MiddlewareKeyNotFound(before);
}
pipeline.Insert(index, configuration);
@@ -114,7 +114,7 @@ public void Append(ReceiveMiddlewareConfiguration configuration, string? after)
var index = pipeline.FindIndex(m => m.Key == after);
if (index == -1)
{
- throw new InvalidOperationException($"Middleware with key {after} not found");
+ throw ThrowHelper.MiddlewareKeyNotFound(after);
}
pipeline.Insert(index + 1, configuration);
@@ -139,7 +139,7 @@ public void Prepend(ReceiveMiddlewareConfiguration configuration, string? before
var index = pipeline.FindIndex(m => m.Key == before);
if (index == -1)
{
- throw new InvalidOperationException($"Middleware with key {before} not found");
+ throw ThrowHelper.MiddlewareKeyNotFound(before);
}
pipeline.Insert(index, configuration);
@@ -198,7 +198,7 @@ public void Append(ConsumerMiddlewareConfiguration configuration, string? after)
var index = pipeline.FindIndex(m => m.Key == after);
if (index == -1)
{
- throw new InvalidOperationException($"Middleware with key {after} not found");
+ throw ThrowHelper.MiddlewareKeyNotFound(after);
}
pipeline.Insert(index + 1, configuration);
@@ -223,7 +223,7 @@ public void Prepend(ConsumerMiddlewareConfiguration configuration, string? befor
var index = pipeline.FindIndex(m => m.Key == before);
if (index == -1)
{
- throw new InvalidOperationException($"Middleware with key {before} not found");
+ throw ThrowHelper.MiddlewareKeyNotFound(before);
}
pipeline.Insert(index, configuration);
diff --git a/src/Mocha/src/Mocha/Middlewares/Receive/CircuitBreaker/CircuitBreakerFeature.cs b/src/Mocha/src/Mocha/Middlewares/Receive/CircuitBreaker/CircuitBreakerFeature.cs
index 2527e6fef66..c19aa83d237 100644
--- a/src/Mocha/src/Mocha/Middlewares/Receive/CircuitBreaker/CircuitBreakerFeature.cs
+++ b/src/Mocha/src/Mocha/Middlewares/Receive/CircuitBreaker/CircuitBreakerFeature.cs
@@ -52,7 +52,7 @@ public void Configure(Action configure)
{
if (IsReadOnly)
{
- throw new InvalidOperationException("The feature is read-only.");
+ throw ThrowHelper.FeatureIsReadOnly();
}
configure(_options);
diff --git a/src/Mocha/src/Mocha/Middlewares/Receive/ConcurrencyLimiter/ConcurrencyLimiterFeature.cs b/src/Mocha/src/Mocha/Middlewares/Receive/ConcurrencyLimiter/ConcurrencyLimiterFeature.cs
index d4ce2662882..76117009e3d 100644
--- a/src/Mocha/src/Mocha/Middlewares/Receive/ConcurrencyLimiter/ConcurrencyLimiterFeature.cs
+++ b/src/Mocha/src/Mocha/Middlewares/Receive/ConcurrencyLimiter/ConcurrencyLimiterFeature.cs
@@ -37,7 +37,7 @@ public void Configure(Action configure)
{
if (IsReadOnly)
{
- throw new InvalidOperationException("The feature is read-only.");
+ throw ThrowHelper.FeatureIsReadOnly();
}
configure(_options);
diff --git a/src/Mocha/src/Mocha/Middlewares/Receive/ReceiveFaultMiddleware.cs b/src/Mocha/src/Mocha/Middlewares/Receive/ReceiveFaultMiddleware.cs
index 619e0b867b8..434a8a14e4c 100644
--- a/src/Mocha/src/Mocha/Middlewares/Receive/ReceiveFaultMiddleware.cs
+++ b/src/Mocha/src/Mocha/Middlewares/Receive/ReceiveFaultMiddleware.cs
@@ -62,7 +62,7 @@ private async ValueTask ReplyToSenderAsync(
if (replyEndpoint is null)
{
// TODO critical error! (Poision Pill)
- throw new InvalidOperationException($"No reply endpoint was found for {replyEndpoint} ");
+ throw ThrowHelper.NoReplyEndpointFound(responseAddress.ToString());
}
var messageType = context.Runtime.GetMessageType(typeof(NotAcknowledgedEvent));
diff --git a/src/Mocha/src/Mocha/Naming/DefaultNamingConventions.cs b/src/Mocha/src/Mocha/Naming/DefaultNamingConventions.cs
index 09128eb70e6..1334d0eab73 100644
--- a/src/Mocha/src/Mocha/Naming/DefaultNamingConventions.cs
+++ b/src/Mocha/src/Mocha/Naming/DefaultNamingConventions.cs
@@ -25,7 +25,7 @@ public string GetReceiveEndpointName(InboundRoute route, ReceiveEndpointKind kin
if (!route.IsInitialized)
{
- throw new InvalidOperationException("Route is not initialized");
+ throw ThrowHelper.RouteNotInitialized();
}
return route.Kind switch
diff --git a/src/Mocha/src/Mocha/Runtime/LazyMessagingRuntime.cs b/src/Mocha/src/Mocha/Runtime/LazyMessagingRuntime.cs
index f1397633148..44a5c2b08f6 100644
--- a/src/Mocha/src/Mocha/Runtime/LazyMessagingRuntime.cs
+++ b/src/Mocha/src/Mocha/Runtime/LazyMessagingRuntime.cs
@@ -8,8 +8,7 @@ public IMessagingRuntime Runtime
{
if (field is null)
{
- throw new InvalidOperationException(
- "Messaging runtime is not initialized, you can only access the runtime after it has been built.");
+ throw ThrowHelper.MessagingRuntimeNotInitialized();
}
return field;
diff --git a/src/Mocha/src/Mocha/Sagas/Descriptors/SagaStateDescriptor.cs b/src/Mocha/src/Mocha/Sagas/Descriptors/SagaStateDescriptor.cs
index 74d820f92c2..33d0930b0fd 100644
--- a/src/Mocha/src/Mocha/Sagas/Descriptors/SagaStateDescriptor.cs
+++ b/src/Mocha/src/Mocha/Sagas/Descriptors/SagaStateDescriptor.cs
@@ -34,8 +34,7 @@ public ISagaTransitionDescriptor OnEvent() where TEvent
if (eventType.IsEventRequest())
{
- throw new InvalidOperationException(
- $"Event type '{eventType}' is a request and should be handled with 'OnRequest' method.");
+ throw ThrowHelper.SagaEventIsRequest(eventType);
}
return On(SagaTransitionKind.Event);
@@ -60,8 +59,7 @@ public ISagaTransitionDescriptor OnReply() where TEvent
if (eventType.IsEventRequest())
{
- throw new InvalidOperationException(
- $"Event type '{eventType}' is a request and should be handled with 'OnRequest' method.");
+ throw ThrowHelper.SagaEventIsRequest(eventType);
}
return On(SagaTransitionKind.Reply);
diff --git a/src/Mocha/src/Mocha/Sagas/Saga.cs b/src/Mocha/src/Mocha/Sagas/Saga.cs
index 5965a44546e..0d758839b99 100644
--- a/src/Mocha/src/Mocha/Sagas/Saga.cs
+++ b/src/Mocha/src/Mocha/Sagas/Saga.cs
@@ -214,7 +214,7 @@ protected Saga()
///
/// Thrown when the saga has not been initialized.
public override IReadOnlyDictionary States
- => _states ?? throw new InvalidOperationException("Saga is not initialized.");
+ => _states ?? throw ThrowHelper.SagaNotInitialized();
///
public override Type StateType => typeof(TState);
@@ -230,7 +230,7 @@ public override async Task HandleEvent(IConsumeContext context)
{
if (_states is null)
{
- throw new InvalidOperationException("Saga is not initialized.");
+ throw ThrowHelper.SagaNotInitialized();
}
var ct = context.CancellationToken;
diff --git a/src/Mocha/src/Mocha/ThrowHelper.cs b/src/Mocha/src/Mocha/ThrowHelper.cs
new file mode 100644
index 00000000000..bb54e267fb9
--- /dev/null
+++ b/src/Mocha/src/Mocha/ThrowHelper.cs
@@ -0,0 +1,166 @@
+namespace Mocha;
+
+internal static class ThrowHelper
+{
+ public static Exception BeforeAndAfterConflict()
+ => new ArgumentException(
+ "Only one of 'before' or 'after' can be specified at the same time.");
+
+ public static Exception MiddlewareKeyNotFound(string key)
+ => new InvalidOperationException($"Middleware with key {key} not found");
+
+ public static Exception RouteEndpointNotConnected()
+ => new InvalidOperationException("Endpoint is not connected");
+
+ public static Exception RouteMustNotBeInitialized()
+ => new InvalidOperationException("Route must not be initialized");
+
+ public static Exception RouteMustBeInitialized()
+ => new InvalidOperationException("Route must be initialized");
+
+ public static Exception RouteMustNotBeCompleted()
+ => new InvalidOperationException("Route must not be completed");
+
+ public static Exception RouteRequiresMessageType()
+ => new InvalidOperationException("Route requires a message type");
+
+ public static Exception RouteRequiresConsumer()
+ => new InvalidOperationException("Route requires a consumer");
+
+ public static Exception RouteNotInitialized()
+ => new InvalidOperationException("Route is not initialized");
+
+ public static Exception TransportConfigurationMissing()
+ => new InvalidOperationException("Could not create configuration for transport");
+
+ public static Exception TransportNameRequired()
+ => new InvalidOperationException("Transport name is required");
+
+ public static Exception TransportSchemaRequired()
+ => new InvalidOperationException("Transport schema is required");
+
+ public static Exception TransportAlreadyStarted()
+ => new InvalidOperationException("Transport is already started");
+
+ public static Exception TransportNotStarted()
+ => new InvalidOperationException("Transport is not started");
+
+ public static Exception EndpointConfigurationFailed()
+ => new InvalidOperationException("Failed to create endpoint configuration");
+
+ public static Exception ReplyConsumerNotFound()
+ => new InvalidOperationException("Reply consumer not found");
+
+ public static Exception FeaturesNotInitialized()
+ => new InvalidOperationException("Features are not initialized");
+
+ public static Exception ConsumerNameRequired()
+ => new InvalidOperationException("Consumer name is null");
+
+ public static Exception HandlerConfigurationMissing()
+ => new InvalidOperationException("Handler configuration is null");
+
+ public static Exception HandlerAlreadyInitialized()
+ => new InvalidOperationException("Handler already initialized");
+
+ public static Exception InvalidHandlerContext()
+ => new InvalidOperationException("Context is not a handler context");
+
+ public static Exception EndpointNameRequired()
+ => new InvalidOperationException("Name is required");
+
+ public static Exception EndpointAlreadyInitialized()
+ => new InvalidOperationException("Endpoint already initialized");
+
+ public static Exception NoTransportForAddress(string address)
+ => new InvalidOperationException($"No transport can handle address: {address}");
+
+ public static Exception EndpointMustBeRegistered()
+ => new InvalidOperationException("Endpoint must be registered before adding addresses");
+
+ public static Exception NoTransportForMessageType(MessageType messageType)
+ => new InvalidOperationException($"No transport can handle message type: {messageType}");
+
+ public static Exception TransportNotFoundForAddress(string address)
+ => new InvalidOperationException($"Transport not found for address {address}");
+
+ public static Exception ReplyDispatchEndpointNotFound(string address)
+ => new InvalidOperationException($"Reply dispatch endpoint not found for address {address}");
+
+ public static Exception NoReplyEndpointFound(string endpoint)
+ => new InvalidOperationException($"No reply endpoint was found for {endpoint}");
+
+ public static Exception UnexpectedResponseType()
+ => new InvalidOperationException("Unexpected response type.");
+
+ public static Exception ResponseIsNull()
+ => new InvalidOperationException("Response is null.");
+
+ public static Exception ResponseBodyNotSet()
+ => new InvalidOperationException("Response body is not set. Could not be parsed.");
+
+ public static Exception PromiseNotFound()
+ => new InvalidOperationException("Promise with correlation ID not found.");
+
+ public static Exception EnvelopeRequired()
+ => new InvalidOperationException("Envelope is required for deserialization");
+
+ public static Exception MessageTypeRequired()
+ => new InvalidOperationException("Message type is required for deserialization");
+
+ public static Exception ContentTypeRequired()
+ => new InvalidOperationException("Content type is required for deserialization");
+
+ public static Exception SerializerNotFound(string messageType, string contentType)
+ => new InvalidOperationException(
+ $"No serializer was found for message type {messageType} and content type {contentType}");
+
+ public static Exception CouldNotDeserializeMessage()
+ => new InvalidOperationException("Could not deserialize message");
+
+ public static Exception DispatchMessageRequired()
+ => new InvalidOperationException(
+ "To send a message either the body must be set or the message must be set");
+
+ public static Exception DispatchMessageTypeRequired()
+ => new InvalidOperationException(
+ "To send a message a message type must be set. Otherwise there is no way to serialize the message");
+
+ public static Exception DispatchContentTypeRequired()
+ => new InvalidOperationException(
+ "To send a message a content type must be set. Otherwise there is no way to serialize the message");
+
+ public static Exception DispatchSerializerNotFound(string contentType, string messageType)
+ => new InvalidOperationException(
+ $"No serializer found for content type {contentType} and message type {messageType}");
+
+ public static Exception TopologyRequired()
+ => new InvalidOperationException("Topology is required");
+
+ public static Exception MessagingRuntimeNotInitialized()
+ => new InvalidOperationException(
+ "Messaging runtime is not initialized, you can only access the runtime after it has been built.");
+
+ public static Exception FailedToCreateConsumer(Type consumerType)
+ => new InvalidOperationException($"Failed to create consumer for type {consumerType}");
+
+ public static Exception InvalidHandlerType()
+ => new InvalidOperationException(
+ "Handler type must be either an event handler, a request handler, or both.");
+
+ public static Exception NoRootServicesFound()
+ => new InvalidOperationException("No root services found");
+
+ public static Exception SagaNotInitialized()
+ => new InvalidOperationException("Saga is not initialized.");
+
+ public static Exception SagaEventIsRequest(Type eventType)
+ => new InvalidOperationException(
+ $"Event type '{eventType}' is a request and should be handled with 'OnRequest' method.");
+
+ public static Exception FeatureIsReadOnly()
+ => new InvalidOperationException("The feature is read-only.");
+
+ public static Exception HostDescriptionMissing()
+ => new InvalidOperationException("Host description is missing.");
+}
diff --git a/src/Mocha/src/Mocha/Topology/TopologyResource~1.cs b/src/Mocha/src/Mocha/Topology/TopologyResource~1.cs
index aef140654db..969bbf97d03 100644
--- a/src/Mocha/src/Mocha/Topology/TopologyResource~1.cs
+++ b/src/Mocha/src/Mocha/Topology/TopologyResource~1.cs
@@ -10,7 +10,7 @@ public abstract class TopologyResource : TopologyResource where T : TopologyC
protected sealed override void OnInitialize(TopologyConfiguration configuration)
{
- Topology = configuration.Topology ?? throw new InvalidOperationException("Topology is required");
+ Topology = configuration.Topology ?? throw ThrowHelper.TopologyRequired();
OnInitialize((T)configuration);
}
diff --git a/src/Mocha/src/Mocha/Transport/MessagingTransport.Lifecyle.cs b/src/Mocha/src/Mocha/Transport/MessagingTransport.Lifecyle.cs
index 76b29ee0527..af9480d1963 100644
--- a/src/Mocha/src/Mocha/Transport/MessagingTransport.Lifecyle.cs
+++ b/src/Mocha/src/Mocha/Transport/MessagingTransport.Lifecyle.cs
@@ -18,11 +18,11 @@ internal void Initialize(IMessagingSetupContext context)
if (Configuration is null)
{
- throw new InvalidOperationException("Could not create configuration for transport");
+ throw ThrowHelper.TransportConfigurationMissing();
}
- Name = Configuration.Name ?? throw new InvalidOperationException("Transport name is required");
- Schema = Configuration.Schema ?? throw new InvalidOperationException("Transport schema is required");
+ Name = Configuration.Name ?? throw ThrowHelper.TransportNameRequired();
+ Schema = Configuration.Schema ?? throw ThrowHelper.TransportSchemaRequired();
Naming = context.Naming;
Conventions = new ConventionRegistry(context.Conventions.Concat(Configuration.Conventions));
Options = Configuration.Options;
@@ -148,7 +148,7 @@ internal void DiscoverEndpoints(IMessagingSetupContext context)
if (replyConsumer is null)
{
- throw new InvalidOperationException("Reply consumer not found");
+ throw ThrowHelper.ReplyConsumerNotFound();
}
var route = new InboundRoute();
@@ -159,7 +159,7 @@ internal void DiscoverEndpoints(IMessagingSetupContext context)
var endpointConfiguration = CreateEndpointConfiguration(context, route);
if (endpointConfiguration is null)
{
- throw new InvalidOperationException("Failed to create endpoint configuration");
+ throw ThrowHelper.EndpointConfigurationFailed();
}
var endpoint = AddEndpoint(context, endpointConfiguration);
diff --git a/src/Mocha/src/Mocha/Transport/MessagingTransport.cs b/src/Mocha/src/Mocha/Transport/MessagingTransport.cs
index 305269b7edb..6e2961c7e46 100644
--- a/src/Mocha/src/Mocha/Transport/MessagingTransport.cs
+++ b/src/Mocha/src/Mocha/Transport/MessagingTransport.cs
@@ -61,7 +61,7 @@ public abstract partial class MessagingTransport : IAsyncDisposable, IFeaturePro
///
/// Thrown if accessed before the transport is initialized.
public IFeatureCollection Features
- => _features ?? throw new InvalidOperationException("Features are not initialized");
+ => _features ?? throw ThrowHelper.FeaturesNotInitialized();
///
/// The messaging topology that describes the transport's addressing structure (exchanges, queues, topics).
@@ -181,7 +181,7 @@ public async ValueTask StartAsync(IMessagingRuntimeContext context, Cancellation
AssertInitialized();
if (IsStarted)
{
- throw new InvalidOperationException("Transport is already started");
+ throw ThrowHelper.TransportAlreadyStarted();
}
await OnBeforeStartAsync(context, cancellationToken);
@@ -204,7 +204,7 @@ public async ValueTask StopAsync(IMessagingRuntimeContext context, CancellationT
{
if (!IsStarted)
{
- throw new InvalidOperationException("Transport is not started");
+ throw ThrowHelper.TransportNotStarted();
}
await OnBeforeStopAsync(cancellationToken);
@@ -253,7 +253,7 @@ public DispatchEndpoint ConnectRoute(IMessagingConfigurationContext context, Out
{
if (CreateEndpointConfiguration(context, route) is not { } configuration)
{
- throw new InvalidOperationException("Failed to create endpoint configuration");
+ throw ThrowHelper.EndpointConfigurationFailed();
}
var endpoint =
@@ -276,7 +276,7 @@ public ReceiveEndpoint ConnectRoute(IMessagingConfigurationContext context, Inbo
{
if (CreateEndpointConfiguration(context, route) is not { } configuration)
{
- throw new InvalidOperationException("Failed to create endpoint configuration");
+ throw ThrowHelper.EndpointConfigurationFailed();
}
var endpoint =
diff --git a/src/Mocha/src/Mocha/Transport/MessagingTransportDescriptor.cs b/src/Mocha/src/Mocha/Transport/MessagingTransportDescriptor.cs
index 24e11ba2d27..3243dbbf7e7 100644
--- a/src/Mocha/src/Mocha/Transport/MessagingTransportDescriptor.cs
+++ b/src/Mocha/src/Mocha/Transport/MessagingTransportDescriptor.cs
@@ -152,8 +152,7 @@ public IMessagingTransportDescriptor UseDispatch(
{
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
@@ -189,8 +188,7 @@ public IMessagingTransportDescriptor UseReceive(
{
if (before is not null && after is not null)
{
- throw new ArgumentException(
- "Only one of 'before' or 'after' can be specified at the same time.");
+ throw ThrowHelper.BeforeAndAfterConflict();
}
if (before is null && after is null)
@@ -215,8 +213,8 @@ public IMessagingTransportDescriptor UseReceive(
/// Returns this descriptor as an extension point for the transport configuration, allowing additional
/// configuration to be layered by external modules.
///
- /// This descriptor cast as .
- public new IDescriptorExtension Extend()
+ /// This descriptor cast as .
+ public new IMessagingDescriptorExtension Extend()
{
return this;
}
@@ -225,10 +223,14 @@ public IMessagingTransportDescriptor UseReceive(
/// Applies an extension configuration delegate to this transport descriptor.
///
/// A delegate that configures the transport through the extension interface.
- /// This descriptor cast as .
- public IDescriptorExtension ExtendWith(
- Action> configure)
+ /// This descriptor cast as .
+ public IMessagingDescriptorExtension ExtendWith(
+ Action> configure)
{
+ ArgumentNullException.ThrowIfNull(configure);
+
+ configure(this);
+
return this;
}
@@ -238,20 +240,15 @@ public IDescriptorExtension ExtendWith(
/// The type of the state object passed to the delegate.
/// A delegate that configures the transport through the extension interface using the provided state.
/// The state object forwarded to the delegate.
- /// This descriptor cast as .
- public IDescriptorExtension ExtendWith(
- Action, TState> configure,
+ /// This descriptor cast as .
+ public IMessagingDescriptorExtension ExtendWith(
+ Action, TState> configure,
TState state)
{
- return this;
- }
+ ArgumentNullException.ThrowIfNull(configure);
- ///
- /// Marks this descriptor as internal, preventing external consumers from using it.
- ///
- /// Always thrown; this method is not yet implemented.
- public void Internal()
- {
- throw new NotImplementedException();
+ configure(this, state);
+
+ return this;
}
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/DiagnosticTests.cs b/src/Mocha/test/Mocha.Analyzers.Tests/DiagnosticTests.cs
index 822745a5d4f..5f64289b575 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/DiagnosticTests.cs
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/DiagnosticTests.cs
@@ -168,4 +168,79 @@ public ValueTask HandleAsync(GenericQuery query, CancellationToken cancell
"""
]).MatchMarkdownAsync();
}
+
+ [Fact]
+ public async Task MO0005_CommandAndNotificationHandler_ReportsError()
+ {
+ await TestHelper.GetGeneratedSourceSnapshot(
+ [
+ """
+ using Mocha.Mediator;
+
+ namespace TestApp;
+
+ public record DoSomethingCommand : ICommand;
+ public record SomethingHappened : INotification;
+
+ public class MultiHandler
+ : ICommandHandler
+ , INotificationHandler
+ {
+ public ValueTask HandleAsync(DoSomethingCommand command, CancellationToken cancellationToken)
+ => default;
+
+ public ValueTask HandleAsync(SomethingHappened notification, CancellationToken cancellationToken)
+ => default;
+ }
+ """
+ ]).MatchMarkdownAsync();
+ }
+
+ [Fact]
+ public async Task MO0005_CommandAndQueryHandler_ReportsError()
+ {
+ await TestHelper.GetGeneratedSourceSnapshot(
+ [
+ """
+ using Mocha.Mediator;
+
+ namespace TestApp;
+
+ public record DoSomethingCommand : ICommand;
+ public record GetSomethingQuery : IQuery;
+
+ public class MultiHandler
+ : ICommandHandler
+ , IQueryHandler
+ {
+ public ValueTask HandleAsync(DoSomethingCommand command, CancellationToken cancellationToken)
+ => default;
+
+ public ValueTask HandleAsync(GetSomethingQuery query, CancellationToken cancellationToken)
+ => new("result");
+ }
+ """
+ ]).MatchMarkdownAsync();
+ }
+
+ [Fact]
+ public async Task NoWarning_SingleHandlerInterface_NoDiagnostic()
+ {
+ await TestHelper.GetGeneratedSourceSnapshot(
+ [
+ """
+ using Mocha.Mediator;
+
+ namespace TestApp;
+
+ public record SomethingHappened : INotification;
+
+ public class SomethingHappenedHandler : INotificationHandler
+ {
+ public ValueTask HandleAsync(SomethingHappened notification, CancellationToken cancellationToken)
+ => default;
+ }
+ """
+ ]).MatchMarkdownAsync();
+ }
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_CommandWithResponseHandler_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_CommandWithResponseHandler_MatchesSnapshot.md
index fe5c2eaffdb..654d92630ef 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_CommandWithResponseHandler_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_CommandWithResponseHandler_MatchesSnapshot.md
@@ -14,22 +14,17 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.CreateOrderHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.CreateOrderHandler),
MessageType = typeof(global::TestApp.CreateOrderCommand),
ResponseType = typeof(int),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_MultipleCommandHandlers_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_MultipleCommandHandlers_MatchesSnapshot.md
index 410dbeb877e..00348330827 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_MultipleCommandHandlers_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_MultipleCommandHandlers_MatchesSnapshot.md
@@ -14,28 +14,25 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.CreateOrderHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.DeleteOrderHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
- MessageType = typeof(global::TestApp.CreateOrderCommand),
- ResponseType = typeof(int),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ HandlerType = typeof(global::TestApp.DeleteOrderHandler),
+ MessageType = typeof(global::TestApp.DeleteOrderCommand),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
- MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ HandlerType = typeof(global::TestApp.CreateOrderHandler),
+ MessageType = typeof(global::TestApp.CreateOrderCommand),
+ ResponseType = typeof(int),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_VoidCommandHandler_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_VoidCommandHandler_MatchesSnapshot.md
index 70db30bd4dc..6df93870771 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_VoidCommandHandler_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/CommandHandlerGeneratorTests.Generate_VoidCommandHandler_MatchesSnapshot.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.DeleteOrderHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.DeleteOrderHandler),
MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0002_CommandWithTwoHandlers_ReportsError.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0002_CommandWithTwoHandlers_ReportsError.md
index 9141fd0c4e9..ba5dc5bd496 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0002_CommandWithTwoHandlers_ReportsError.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0002_CommandWithTwoHandlers_ReportsError.md
@@ -16,29 +16,26 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.CreateOrderHandlerA), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.CreateOrderHandlerB), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.CreateOrderHandlerA),
MessageType = typeof(global::TestApp.CreateOrderCommand),
ResponseType = typeof(int),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.CreateOrderHandlerB),
MessageType = typeof(global::TestApp.CreateOrderCommand),
ResponseType = typeof(int),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0002_VoidCommandWithTwoHandlers_ReportsError.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0002_VoidCommandWithTwoHandlers_ReportsError.md
index f6e58be366c..d4b08ea56db 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0002_VoidCommandWithTwoHandlers_ReportsError.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0002_VoidCommandWithTwoHandlers_ReportsError.md
@@ -16,27 +16,24 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.DeleteOrderHandlerA), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.DeleteOrderHandlerB), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.DeleteOrderHandlerA),
MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.DeleteOrderHandlerB),
MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0004_OpenGenericCommand_ReportsInfo.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0004_OpenGenericCommand_ReportsInfo.md
index c46621cff0d..7648ba7cb15 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0004_OpenGenericCommand_ReportsInfo.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0004_OpenGenericCommand_ReportsInfo.md
@@ -16,21 +16,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler>), typeof(global::TestApp.GenericCommandHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration>(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.GenericCommandHandler),
MessageType = typeof(global::TestApp.GenericCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal>()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline, global::TestApp.GenericCommand>()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0004_OpenGenericQuery_ReportsInfo.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0004_OpenGenericQuery_ReportsInfo.md
index 0e179f97bda..79a9e35d339 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0004_OpenGenericQuery_ReportsInfo.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0004_OpenGenericQuery_ReportsInfo.md
@@ -16,22 +16,17 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.IQueryHandler, T>), typeof(global::TestApp.GenericQueryHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration>(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.GenericQueryHandler),
MessageType = typeof(global::TestApp.GenericQuery),
ResponseType = typeof(T),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildQueryTerminal, T>()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Query,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildQueryPipeline, global::TestApp.GenericQuery, T>()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0005_CommandAndNotificationHandler_ReportsError.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0005_CommandAndNotificationHandler_ReportsError.md
new file mode 100644
index 00000000000..dc0172c7eb1
--- /dev/null
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0005_CommandAndNotificationHandler_ReportsError.md
@@ -0,0 +1,28 @@
+# MO0005_CommandAndNotificationHandler_ReportsError
+
+```json
+[
+ {
+ "Id": "MO0001",
+ "Title": "Missing handler for message type",
+ "Severity": "Warning",
+ "WarningLevel": 1,
+ "Location": ": (4,14)-(4,32)",
+ "MessageFormat": "Message type '{0}' has no registered handler",
+ "Message": "Message type 'global::TestApp.DoSomethingCommand' has no registered handler",
+ "Category": "Mediator",
+ "CustomTags": []
+ },
+ {
+ "Id": "MO0005",
+ "Title": "Handler implements multiple mediator handler interfaces",
+ "Severity": "Error",
+ "WarningLevel": 0,
+ "Location": ": (7,13)-(7,25)",
+ "MessageFormat": "Handler '{0}' must implement exactly one mediator handler interface",
+ "Message": "Handler 'global::TestApp.MultiHandler' must implement exactly one mediator handler interface",
+ "Category": "Mediator",
+ "CustomTags": []
+ }
+]
+```
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0005_CommandAndQueryHandler_ReportsError.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0005_CommandAndQueryHandler_ReportsError.md
new file mode 100644
index 00000000000..08197a0eb74
--- /dev/null
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.MO0005_CommandAndQueryHandler_ReportsError.md
@@ -0,0 +1,39 @@
+# MO0005_CommandAndQueryHandler_ReportsError
+
+```json
+[
+ {
+ "Id": "MO0001",
+ "Title": "Missing handler for message type",
+ "Severity": "Warning",
+ "WarningLevel": 1,
+ "Location": ": (4,14)-(4,32)",
+ "MessageFormat": "Message type '{0}' has no registered handler",
+ "Message": "Message type 'global::TestApp.DoSomethingCommand' has no registered handler",
+ "Category": "Mediator",
+ "CustomTags": []
+ },
+ {
+ "Id": "MO0001",
+ "Title": "Missing handler for message type",
+ "Severity": "Warning",
+ "WarningLevel": 1,
+ "Location": ": (5,14)-(5,31)",
+ "MessageFormat": "Message type '{0}' has no registered handler",
+ "Message": "Message type 'global::TestApp.GetSomethingQuery' has no registered handler",
+ "Category": "Mediator",
+ "CustomTags": []
+ },
+ {
+ "Id": "MO0005",
+ "Title": "Handler implements multiple mediator handler interfaces",
+ "Severity": "Error",
+ "WarningLevel": 0,
+ "Location": ": (7,13)-(7,25)",
+ "MessageFormat": "Handler '{0}' must implement exactly one mediator handler interface",
+ "Message": "Handler 'global::TestApp.MultiHandler' must implement exactly one mediator handler interface",
+ "Category": "Mediator",
+ "CustomTags": []
+ }
+]
+```
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.NoWarning_CommandWithHandler_NoDiagnostic.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.NoWarning_CommandWithHandler_NoDiagnostic.md
index 68a1e39dfd0..58fa8e23493 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.NoWarning_CommandWithHandler_NoDiagnostic.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.NoWarning_CommandWithHandler_NoDiagnostic.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.DeleteOrderHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.DeleteOrderHandler),
MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.NoWarning_SingleHandlerInterface_NoDiagnostic.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.NoWarning_SingleHandlerInterface_NoDiagnostic.md
new file mode 100644
index 00000000000..4f61ee3eb4f
--- /dev/null
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/DiagnosticTests.NoWarning_SingleHandlerInterface_NoDiagnostic.md
@@ -0,0 +1,33 @@
+# NoWarning_SingleHandlerInterface_NoDiagnostic
+
+```csharp
+//
+
+#nullable enable
+#pragma warning disable
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ [global::System.CodeDom.Compiler.GeneratedCode("Mocha.Analyzers", "1.0.0")]
+ public static class TestsMediatorBuilderExtensions
+ {
+ public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
+ this global::Mocha.Mediator.IMediatorHostBuilder builder)
+ {
+
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
+ {
+ HandlerType = typeof(global::TestApp.SomethingHappenedHandler),
+ MessageType = typeof(global::TestApp.SomethingHappened),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Notification,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildNotificationPipeline()
+ });
+
+ return builder;
+ }
+ }
+}
+
+```
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ExplicitModuleNameTests.Generate_ModuleWithOnlyName_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ExplicitModuleNameTests.Generate_ModuleWithOnlyName_MatchesSnapshot.md
index 430222bdaf2..7ed08a51542 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ExplicitModuleNameTests.Generate_ModuleWithOnlyName_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ExplicitModuleNameTests.Generate_ModuleWithOnlyName_MatchesSnapshot.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTest2(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.DeleteOrderHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.DeleteOrderHandler),
MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/GenericHandlerTests.Generate_MultipleHandlersSameNamespace_DeterministicOrder_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/GenericHandlerTests.Generate_MultipleHandlersSameNamespace_DeterministicOrder_MatchesSnapshot.md
index 3621633e3e4..5ba352e34ae 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/GenericHandlerTests.Generate_MultipleHandlersSameNamespace_DeterministicOrder_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/GenericHandlerTests.Generate_MultipleHandlersSameNamespace_DeterministicOrder_MatchesSnapshot.md
@@ -14,33 +14,32 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.AlphaHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.MidHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.ZetaHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.AlphaHandler),
MessageType = typeof(global::TestApp.AlphaCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.MidHandler),
MessageType = typeof(global::TestApp.MidCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.ZetaHandler),
MessageType = typeof(global::TestApp.ZetaCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/GenericHandlerTests.Generate_OpenGenericCommand_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/GenericHandlerTests.Generate_OpenGenericCommand_MatchesSnapshot.md
index 3102c237869..0fe2f8de85e 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/GenericHandlerTests.Generate_OpenGenericCommand_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/GenericHandlerTests.Generate_OpenGenericCommand_MatchesSnapshot.md
@@ -16,22 +16,17 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler, string>), typeof(global::TestApp.StringProcessor), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.StringProcessor),
MessageType = typeof(global::TestApp.ProcessCommand),
ResponseType = typeof(string),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal, string>()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline, string>()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/InternalHandlerTests.Generate_InternalHandler_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/InternalHandlerTests.Generate_InternalHandler_MatchesSnapshot.md
index 7ba89aea38e..815a1841400 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/InternalHandlerTests.Generate_InternalHandler_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/InternalHandlerTests.Generate_InternalHandler_MatchesSnapshot.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTest(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.DeleteOrderHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.DeleteOrderHandler),
MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_CommandOfTResolution_ICommandGeneric_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_CommandOfTResolution_ICommandGeneric_MatchesSnapshot.md
index 6821e52fcd6..c3658a948c2 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_CommandOfTResolution_ICommandGeneric_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_CommandOfTResolution_ICommandGeneric_MatchesSnapshot.md
@@ -14,22 +14,17 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.ComputeHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.ComputeHandler),
MessageType = typeof(global::TestApp.ComputeCommand),
ResponseType = typeof(long),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_CommandVoidResolution_ICommandInterface_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_CommandVoidResolution_ICommandInterface_MatchesSnapshot.md
index 3b4736118fb..ceb0ea09039 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_CommandVoidResolution_ICommandInterface_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_CommandVoidResolution_ICommandInterface_MatchesSnapshot.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.FireAndForgetHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.FireAndForgetHandler),
MessageType = typeof(global::TestApp.FireAndForgetCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_WithAllHandlerTypes_AllSymbolsResolved_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_WithAllHandlerTypes_AllSymbolsResolved_MatchesSnapshot.md
index f9e0b887e29..d73bd07dc3c 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_WithAllHandlerTypes_AllSymbolsResolved_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/KnownTypeSymbolsTests.Generate_WithAllHandlerTypes_AllSymbolsResolved_MatchesSnapshot.md
@@ -14,43 +14,42 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.ResponseCommandHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.VoidCommandHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.IQueryHandler), typeof(global::TestApp.MyQueryHandler), lifetime));
-
- // Register notification handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddEnumerable(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.INotificationHandler), typeof(global::TestApp.MyEventHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
- MessageType = typeof(global::TestApp.ResponseCommand),
- ResponseType = typeof(int),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ HandlerType = typeof(global::TestApp.VoidCommandHandler),
+ MessageType = typeof(global::TestApp.VoidCommand),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
- MessageType = typeof(global::TestApp.VoidCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ HandlerType = typeof(global::TestApp.ResponseCommandHandler),
+ MessageType = typeof(global::TestApp.ResponseCommand),
+ ResponseType = typeof(int),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.MyQueryHandler),
MessageType = typeof(global::TestApp.MyQuery),
ResponseType = typeof(string),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildQueryTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Query,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildQueryPipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.MyEventHandler),
MessageType = typeof(global::TestApp.MyEvent),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildNotificationTerminal(new global::System.Type[] { typeof(global::TestApp.MyEventHandler) })
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Notification,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildNotificationPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_DefaultAssemblyName_PrefixesWithLastSegment.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_DefaultAssemblyName_PrefixesWithLastSegment.md
index fe8de69d484..72c045f859b 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_DefaultAssemblyName_PrefixesWithLastSegment.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_DefaultAssemblyName_PrefixesWithLastSegment.md
@@ -14,22 +14,17 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.IQueryHandler), typeof(global::TestApp.GetItemHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.GetItemHandler),
MessageType = typeof(global::TestApp.GetItemQuery),
ResponseType = typeof(string),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildQueryTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Query,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildQueryPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_DottedAssemblyName_UsesLastSegment.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_DottedAssemblyName_UsesLastSegment.md
index 43b370701c5..e6fcf81e6b8 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_DottedAssemblyName_UsesLastSegment.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_DottedAssemblyName_UsesLastSegment.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddOrdering(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.PingHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.PingHandler),
MessageType = typeof(global::TestApp.PingCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_ModuleFile_ContainsHandlerRegistrations.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_ModuleFile_ContainsHandlerRegistrations.md
index d59668ec973..3d9cdda9749 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_ModuleFile_ContainsHandlerRegistrations.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MediatorModuleTests.Generate_ModuleFile_ContainsHandlerRegistrations.md
@@ -14,22 +14,17 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.CreateInvoiceHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.CreateInvoiceHandler),
MessageType = typeof(global::TestApp.CreateInvoiceCommand),
ResponseType = typeof(int),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MixedHandlerGeneratorTests.Generate_AllHandlerTypes_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MixedHandlerGeneratorTests.Generate_AllHandlerTypes_MatchesSnapshot.md
index f21d20f3a66..bf2d65a1323 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MixedHandlerGeneratorTests.Generate_AllHandlerTypes_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MixedHandlerGeneratorTests.Generate_AllHandlerTypes_MatchesSnapshot.md
@@ -14,44 +14,50 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.CreateOrderHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.DeleteOrderHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.IQueryHandler), typeof(global::TestApp.GetUserHandler), lifetime));
-
- // Register notification handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddEnumerable(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.INotificationHandler), typeof(global::TestApp.OrderCreatedEmailHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddEnumerable(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.INotificationHandler), typeof(global::TestApp.OrderCreatedStatsHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
- MessageType = typeof(global::TestApp.CreateOrderCommand),
- ResponseType = typeof(int),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ HandlerType = typeof(global::TestApp.DeleteOrderHandler),
+ MessageType = typeof(global::TestApp.DeleteOrderCommand),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
- MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ HandlerType = typeof(global::TestApp.CreateOrderHandler),
+ MessageType = typeof(global::TestApp.CreateOrderCommand),
+ ResponseType = typeof(int),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.GetUserHandler),
MessageType = typeof(global::TestApp.GetUserQuery),
ResponseType = typeof(global::TestApp.UserDto),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildQueryTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Query,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildQueryPipeline()
+ });
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
+ {
+ HandlerType = typeof(global::TestApp.OrderCreatedEmailHandler),
+ MessageType = typeof(global::TestApp.OrderCreated),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Notification,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildNotificationPipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.OrderCreatedStatsHandler),
MessageType = typeof(global::TestApp.OrderCreated),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildNotificationTerminal(new global::System.Type[] { typeof(global::TestApp.OrderCreatedEmailHandler), typeof(global::TestApp.OrderCreatedStatsHandler) })
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Notification,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildNotificationPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MixedHandlerGeneratorTests.Generate_HandlersInDifferentNamespaces_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MixedHandlerGeneratorTests.Generate_HandlersInDifferentNamespaces_MatchesSnapshot.md
index e650b0f3d7f..64c2a35c3fc 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MixedHandlerGeneratorTests.Generate_HandlersInDifferentNamespaces_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/MixedHandlerGeneratorTests.Generate_HandlersInDifferentNamespaces_MatchesSnapshot.md
@@ -14,29 +14,26 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.Orders.CreateOrderHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.IQueryHandler), typeof(global::TestApp.Users.GetUserHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.Orders.CreateOrderHandler),
MessageType = typeof(global::TestApp.Orders.CreateOrderCommand),
ResponseType = typeof(int),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.CommandResponse,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandResponsePipeline()
});
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.Users.GetUserHandler),
MessageType = typeof(global::TestApp.Users.GetUserQuery),
ResponseType = typeof(global::TestApp.Users.UserDto),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildQueryTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Query,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildQueryPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ModuleNameHelperTests.Generate_AssemblyNameWithHyphen_UsesLastSegmentSanitized_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ModuleNameHelperTests.Generate_AssemblyNameWithHyphen_UsesLastSegmentSanitized_MatchesSnapshot.md
index 1c863cb9cbb..0cbb349af3e 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ModuleNameHelperTests.Generate_AssemblyNameWithHyphen_UsesLastSegmentSanitized_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ModuleNameHelperTests.Generate_AssemblyNameWithHyphen_UsesLastSegmentSanitized_MatchesSnapshot.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddOrder_Processing(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.PingHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.PingHandler),
MessageType = typeof(global::TestApp.PingCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ModuleNameHelperTests.Generate_NullAssemblyName_UsesAssemblyDefault_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ModuleNameHelperTests.Generate_NullAssemblyName_UsesAssemblyDefault_MatchesSnapshot.md
index 9a83b4cf1f1..e1d6e37457a 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ModuleNameHelperTests.Generate_NullAssemblyName_UsesAssemblyDefault_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/ModuleNameHelperTests.Generate_NullAssemblyName_UsesAssemblyDefault_MatchesSnapshot.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddUnknown(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.PingHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.PingHandler),
MessageType = typeof(global::TestApp.PingCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/NestedHandlerTests.Generate_NestedClassHandler_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/NestedHandlerTests.Generate_NestedClassHandler_MatchesSnapshot.md
index 053cf099324..212d88fc672 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/NestedHandlerTests.Generate_NestedClassHandler_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/NestedHandlerTests.Generate_NestedClassHandler_MatchesSnapshot.md
@@ -14,21 +14,16 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAdd(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.ICommandHandler), typeof(global::TestApp.Outer.DeleteOrderHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.Outer.DeleteOrderHandler),
MessageType = typeof(global::TestApp.DeleteOrderCommand),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildVoidCommandTerminal()
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Command,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildCommandPipeline()
});
- });
return builder;
}
diff --git a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/NotificationHandlerGeneratorTests.Generate_MultipleHandlersForSameNotification_MatchesSnapshot.md b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/NotificationHandlerGeneratorTests.Generate_MultipleHandlersForSameNotification_MatchesSnapshot.md
index 6be1a697c41..1412f566583 100644
--- a/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/NotificationHandlerGeneratorTests.Generate_MultipleHandlersForSameNotification_MatchesSnapshot.md
+++ b/src/Mocha/test/Mocha.Analyzers.Tests/__snapshots__/NotificationHandlerGeneratorTests.Generate_MultipleHandlersForSameNotification_MatchesSnapshot.md
@@ -14,22 +14,24 @@ namespace Microsoft.Extensions.DependencyInjection
public static global::Mocha.Mediator.IMediatorHostBuilder AddTests(
this global::Mocha.Mediator.IMediatorHostBuilder builder)
{
- var services = builder.Services;
- var lifetime = builder.Options.ServiceLifetime;
- // Register notification handlers
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddEnumerable(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.INotificationHandler), typeof(global::TestApp.SendEmailHandler), lifetime));
- global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddEnumerable(services, new global::Microsoft.Extensions.DependencyInjection.ServiceDescriptor(typeof(global::Mocha.Mediator.INotificationHandler), typeof(global::TestApp.UpdateStatsHandler), lifetime));
-
- // Register pipelines
- global::Mocha.Mediator.MediatorHostBuilderExtensions.ConfigureMediator(builder, static b =>
- {
- b.RegisterPipeline(new global::Mocha.Mediator.MediatorPipelineConfiguration
+ // Register handler configurations
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
+ {
+ HandlerType = typeof(global::TestApp.SendEmailHandler),
+ MessageType = typeof(global::TestApp.OrderCreated),
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Notification,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildNotificationPipeline()
+ });
+ global::Mocha.Mediator.MediatorHostBuilderHandlerExtensions.AddHandlerConfiguration(builder,
+ new global::Mocha.Mediator.MediatorHandlerConfiguration
{
+ HandlerType = typeof(global::TestApp.UpdateStatsHandler),
MessageType = typeof(global::TestApp.OrderCreated),
- Terminal = global::Mocha.Mediator.PipelineBuilder.BuildNotificationTerminal(new global::System.Type[] { typeof(global::TestApp.SendEmailHandler), typeof(global::TestApp.UpdateStatsHandler) })
+ Kind = global::Mocha.Mediator.MediatorHandlerKind.Notification,
+ Delegate = global::Mocha.Mediator.PipelineBuilder.BuildNotificationPipeline