├── .config └── dotnet-tools.json ├── .csharpierignore ├── .csharpierrc.json ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .husky ├── pre-commit └── task-runner.json ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── Directory.Build.props ├── Directory.Packages.props ├── Justfile ├── LICENSE ├── Mediator.sln ├── NuGet.config ├── README.md ├── benchmarks ├── Directory.Build.props ├── Justfile ├── Mediator.Benchmarks │ ├── CustomColumn.cs │ ├── Internal │ │ ├── SwitchesAndJumpTables.cs │ │ └── Unboxing.cs │ ├── Mediator.Benchmarks.csproj │ ├── Messaging │ │ ├── Comparison │ │ │ ├── ComparisonBenchmarks.cs │ │ │ └── HeadlineBenchmarks.cs │ │ ├── Fixture.cs │ │ ├── NotificationBenchmarks.cs │ │ ├── RequestBenchmarks.cs │ │ └── StreamingBenchmarks.cs │ ├── Program.cs │ └── SourceGenerator │ │ └── SourceGeneratorBenchmark.cs ├── README.md └── cmp.py ├── common ├── Messages.cs ├── Options.cs └── README.md ├── global.json ├── img ├── benchmarks.png ├── missing_request_handler.png └── multiple_request_handlers.png ├── samples ├── Directory.Build.props ├── Directory.Packages.props ├── README.md ├── Showcase │ ├── Program.cs │ ├── README.md │ └── Showcase.csproj ├── apps │ ├── ASPNET_CORE_Blazor │ │ ├── AspNetCoreBlazor.Client │ │ │ ├── AspNetCoreBlazor.Client.csproj │ │ │ ├── Pages │ │ │ │ ├── Counter.cs │ │ │ │ └── Counter.razor │ │ │ ├── Program.cs │ │ │ ├── _Imports.razor │ │ │ └── wwwroot │ │ │ │ ├── appsettings.Development.json │ │ │ │ └── appsettings.json │ │ ├── AspNetCoreBlazor.sln │ │ ├── AspNetCoreBlazor │ │ │ ├── AspNetCoreBlazor.csproj │ │ │ ├── Components │ │ │ │ ├── App.razor │ │ │ │ ├── Layout │ │ │ │ │ ├── MainLayout.razor │ │ │ │ │ ├── MainLayout.razor.css │ │ │ │ │ ├── NavMenu.razor │ │ │ │ │ └── NavMenu.razor.css │ │ │ │ ├── Pages │ │ │ │ │ ├── Error.razor │ │ │ │ │ ├── Home.razor │ │ │ │ │ ├── Weather.cs │ │ │ │ │ └── Weather.razor │ │ │ │ ├── Routes.razor │ │ │ │ └── _Imports.razor │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ └── wwwroot │ │ │ │ ├── app.css │ │ │ │ ├── bootstrap │ │ │ │ ├── bootstrap.min.css │ │ │ │ └── bootstrap.min.css.map │ │ │ │ └── favicon.png │ │ └── README.md │ ├── ASPNET_Core │ │ ├── ASPNET_Core.csproj │ │ ├── Controllers │ │ │ └── WeatherForecastController.cs │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── WeatherForecasts │ │ │ ├── GetWeatherForecasts.cs │ │ │ └── WeatherForecast.cs │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ ├── ASPNET_Core_CleanArchitecture │ │ ├── AspNetCoreSample.Api │ │ │ ├── AspNetCoreSample.Api.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── TodoApi.cs │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ │ ├── AspNetCoreSample.Application │ │ │ ├── AspNetCoreSample.Application.csproj │ │ │ ├── IValidate.cs │ │ │ ├── Pipeline │ │ │ │ ├── ErrorLoggingBehaviour.cs │ │ │ │ └── MessageValidatorBehaviour.cs │ │ │ ├── ServiceCollectionExtensions.cs │ │ │ ├── Todos │ │ │ │ ├── AddTodoItem.cs │ │ │ │ ├── GetTodoItems.cs │ │ │ │ └── ITodoItemRepository.cs │ │ │ ├── ValidationError.cs │ │ │ └── ValidationException.cs │ │ ├── AspNetCoreSample.Domain │ │ │ ├── AspNetCoreSample.Domain.csproj │ │ │ └── TodoItem.cs │ │ ├── AspNetCoreSample.Infrastructure │ │ │ ├── AspNetCoreSample.Infrastructure.csproj │ │ │ ├── InMemoryTodoItemRepository.cs │ │ │ └── ServiceCollectionExtensions.cs │ │ ├── README.md │ │ └── post-todo.http │ ├── ASPNET_Core_Indirect │ │ ├── AspNetCoreIndirect.Application │ │ │ ├── AspNetCoreIndirect.Application.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ │ └── launchSettings.json │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ │ ├── AspNetCoreIndirect.BaseClasses │ │ │ ├── ApplicationController.cs │ │ │ ├── ApplicationHandler.cs │ │ │ ├── ApplicationRequest.cs │ │ │ └── AspNetCoreIndirect.BaseClasses.csproj │ │ ├── AspNetCoreIndirect.Implementations │ │ │ ├── AspNetCoreIndirect.Implementations.csproj │ │ │ ├── GetWeatherForecast.cs │ │ │ ├── GetWeatherForecastHandler.cs │ │ │ ├── WeatherForecast.cs │ │ │ └── WeatherForecastController.cs │ │ ├── README.md │ │ └── get-weather-forecast.http │ └── InternalMessages │ │ ├── InternalMessages.Api │ │ ├── InternalMessages.Api.csproj │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── appsettings.Development.json │ │ └── appsettings.json │ │ ├── InternalMessages.Application │ │ ├── InternalMessages.Application.csproj │ │ └── PingHandler.cs │ │ └── InternalMessages.Domain │ │ ├── InternalMessages.Domain.csproj │ │ └── Ping.cs ├── basic │ ├── Console │ │ ├── Console.csproj │ │ ├── Program.cs │ │ └── README.md │ ├── ConsoleAOT │ │ ├── ConsoleAOT.csproj │ │ ├── Program.cs │ │ └── README.md │ ├── NetFramework │ │ ├── Console.sln │ │ ├── Console │ │ │ ├── App.config │ │ │ ├── Console.csproj │ │ │ ├── Program.cs │ │ │ └── Properties │ │ │ │ └── AssemblyInfo.cs │ │ └── Directory.Build.props │ ├── NotificationPublisher │ │ ├── NotificationPublisher.csproj │ │ ├── Program.cs │ │ └── README.md │ ├── Notifications │ │ ├── Notifications.csproj │ │ ├── Program.cs │ │ └── README.md │ └── Streaming │ │ ├── Program.cs │ │ ├── README.md │ │ └── Streaming.csproj └── use-cases │ ├── Autofac_DI │ ├── Autofac_DI.csproj │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── README.md │ ├── appsettings.Development.json │ └── appsettings.json │ └── MassTransitIntegration │ ├── MassTransitIntegration.csproj │ └── Program.cs ├── src ├── Directory.Build.props ├── Mediator.SourceGenerator │ ├── Implementation │ │ ├── Analysis │ │ │ ├── CompilationAnalyzer.cs │ │ │ ├── InvocationSyntaxReceiver.cs │ │ │ ├── MessageHandler.cs │ │ │ ├── NotificationMessage.cs │ │ │ ├── NotificationMessageHandler.cs │ │ │ ├── PipelineBehaviorType.cs │ │ │ ├── RequestMessage.cs │ │ │ ├── RequestMessageHandler.cs │ │ │ └── SymbolMetadata.cs │ │ ├── Constants.cs │ │ ├── Diagnostics.cs │ │ ├── EmbeddedResource.cs │ │ ├── Extensions │ │ │ └── RoslynExtensions.cs │ │ ├── ImmutableEquatableArray.cs │ │ ├── MediatorGenerationContext.cs │ │ ├── MediatorImplementationGenerator.cs │ │ ├── MediatorOptionsGenerator.cs │ │ ├── Models │ │ │ ├── CompilationModel.cs │ │ │ ├── NotificationMessageHandlerModel.cs │ │ │ ├── NotificationMessageModel.cs │ │ │ ├── NotificationPublisherTypeModel.cs │ │ │ ├── PipelineBehaviorModel.cs │ │ │ ├── RequestMessageHandlerModel.cs │ │ │ ├── RequestMessageHandlerWrapperModel.cs │ │ │ ├── RequestMessageModel.cs │ │ │ └── SymbolMetadataModel.cs │ │ ├── Versioning.cs │ │ └── resources │ │ │ ├── AssemblyReference.sbn-cs │ │ │ ├── Mediator.sbn-cs │ │ │ ├── MediatorFallback.sbn-cs │ │ │ ├── MediatorOptions.sbn-cs │ │ │ └── MediatorOptionsAttribute.sbn-cs │ ├── IncrementalMediatorGenerator.cs │ ├── Mediator.SourceGenerator.csproj │ ├── MediatorGeneratorStepName.cs │ └── Properties │ │ └── launchSettings.json └── Mediator │ ├── Handlers │ ├── ICommandHandler.cs │ ├── INotificationHandler.cs │ ├── IQueryHandler.cs │ ├── IRequestHandler.cs │ ├── IStreamCommandHandler.cs │ ├── IStreamQueryHandler.cs │ └── IStreamRequestHandler.cs │ ├── IMediator.cs │ ├── INotificationPublisher.cs │ ├── IPublisher.cs │ ├── ISender.cs │ ├── InvalidMessageException.cs │ ├── Mediator.csproj │ ├── Messages │ ├── IBaseCommand.cs │ ├── IBaseQuery.cs │ ├── IBaseRequest.cs │ ├── ICommand.cs │ ├── IMessage.cs │ ├── INotification.cs │ ├── IQuery.cs │ ├── IRequest.cs │ ├── IStreamCommand.cs │ ├── IStreamMessage.cs │ ├── IStreamQuery.cs │ ├── IStreamRequest.cs │ └── Unit.cs │ ├── MissingMessageHandlerException.cs │ ├── Module.cs │ ├── Pipeline │ ├── IPipelineBehavior.cs │ ├── IStreamPipelineBehavior.cs │ ├── MessageExceptionHandler.cs │ ├── MessageHandlerDelegate.cs │ ├── MessagePostProcessor.cs │ ├── MessagePreProcessor.cs │ └── StreamHandlerDelegate.cs │ ├── Publishers │ ├── ForeachAwaitPublisher.cs │ └── TaskWhenAllPublisher.cs │ └── ThrowHelper.cs ├── test ├── Directory.Build.props ├── Mediator.MemAllocationTests │ ├── Allocations.cs │ ├── Mediator.MemAllocationTests.csproj │ ├── NonParallelCollectionDefinitionClass.cs │ ├── NotificationTests.cs │ ├── RequestTests.cs │ ├── SomeNotificationMemAllocTracking.cs │ └── SomeRequestMemAllocTracking.cs ├── Mediator.SmokeTestConsole │ ├── Mediator.SmokeTestConsole.csproj │ └── Program.cs ├── Mediator.SourceGenerator.Tests │ ├── AssertExtensions.cs │ ├── Assertions.cs │ ├── BasicTests.cs │ ├── ConfigurationTests.cs │ ├── Fixture.cs │ ├── GeneratorResult.cs │ ├── Incremental │ │ ├── IncrementalGeneratorRunReason.cs │ │ ├── IncrementalGeneratorTest.cs │ │ └── TestHelper.cs │ ├── LifetimeOptionTests.cs │ ├── Mediator.SourceGenerator.Tests.csproj │ ├── MessageOrderingTests.cs │ ├── ModuleInitializer.cs │ ├── NotificationsTests.cs │ ├── ProjectTypeTests.cs │ ├── ReportingTests.cs │ ├── SampleTests.cs │ ├── _snapshots │ │ ├── BasicTests.Multiple_Notification_Handlers_One_Class#Mediator.g.verified.cs │ │ ├── BasicTests.Multiple_Request_Handlers_One_Class#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_Assemblies_Duplicate_Configuration#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_Assemblies_Duplicate_Configuration.verified.txt │ │ ├── ConfigurationTests.Test_Assemblies_Duplicate_Reference#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_Assemblies_Duplicate_Reference.verified.txt │ │ ├── ConfigurationTests.Test_Assemblies_Invalid_Reference#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_Assemblies_Invalid_Reference.verified.txt │ │ ├── ConfigurationTests.Test_Assemblies_Mediator_Abstractions_Reference#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_Assemblies_Mediator_Abstractions_Reference.verified.txt │ │ ├── ConfigurationTests.Test_Assemblies_TypeOf#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_Assemblies_TypeOf_Assembly#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_Assemblies_Valid_Reference#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_ForEachAwaitPublisher_Default#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_Non_Literal#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_Non_Literal.verified.txt │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_value=False#AssemblyReference.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_value=False#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_value=False#MediatorOptions.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_value=False#MediatorOptionsAttribute.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_value=True#AssemblyReference.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_value=True#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_value=True#MediatorOptions.g.verified.cs │ │ ├── ConfigurationTests.Test_GenerateTypesAsInternal_value=True#MediatorOptionsAttribute.g.verified.cs │ │ ├── ConfigurationTests.Test_PipelineBehaviors#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_PipelineBehaviors_Invalid_Types#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_PipelineBehaviors_Invalid_Types.verified.txt │ │ ├── ConfigurationTests.Test_TaskWhenAllPublisher_For_Notifications_AddMediator#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_TaskWhenAllPublisher_For_Notifications_Attribute#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_TaskWhenAllPublisher_For_Notifications_Program#Mediator.g.verified.cs │ │ ├── ConfigurationTests.Test_TaskWhenAllPublisher_For_Notifications_Program#MediatorOptions.g.verified.cs │ │ ├── ConfigurationTests.Test_TaskWhenAllPublisher_For_Notifications_Program#MediatorOptionsAttribute.g.verified.cs │ │ ├── LifetimeOptionTests.Test_No_Args#Mediator.g.verified.cs │ │ ├── LifetimeOptionTests.Test_Transient_Lifetime_With_Named_Namespace_Arg#Mediator.g.verified.cs │ │ ├── MessageOrderingTests.Test_Notifications_Ordering#Mediator.g.verified.cs │ │ ├── MessageOrderingTests.Test_Notifications_Ordering_Bigger#Mediator.g.verified.cs │ │ ├── NotificationsTests.Test_Notifications_DI#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Command_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Command_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Command_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Notification_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Notification_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Notification_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Query_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Query_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Query_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Request_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Request_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=Request_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamCommand_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamCommand_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamCommand_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamQuery_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamQuery_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamQuery_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamRequest_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamRequest_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Size_Uneven_manyOfMessageType=StreamRequest_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Sizes_n=16_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Sizes_n=16_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Sizes_n=16_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Sizes_n=17_serviceLifetime=Scoped#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Sizes_n=17_serviceLifetime=Singleton#Mediator.g.verified.cs │ │ ├── ProjectTypeTests.Test_Project_Sizes_n=17_serviceLifetime=Transient#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Abstract_Handler_Program#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Abstract_Handler_Program.verified.txt │ │ ├── ReportingTests.Test_Byte_Array_Response_Program#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Cast_Lifetime_Config#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Cast_Lifetime_Config.verified.txt │ │ ├── ReportingTests.Test_Configuratoin_Conflict#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Configuratoin_Conflict.verified.txt │ │ ├── ReportingTests.Test_Const_Variable_In_Config#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Deep_Namespace_Program#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Duplicate_Handlers#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Duplicate_Handlers.verified.txt │ │ ├── ReportingTests.Test_Empty_Program#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Invalid_Handler_Type#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Invalid_Handler_Type.verified.txt │ │ ├── ReportingTests.Test_Invalid_Variable_In_Config#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Invalid_Variable_In_Config.verified.txt │ │ ├── ReportingTests.Test_Local_Literal_Variable_In_Config#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Local_Variables_Referencing_Consts_Config#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Multiple_AddMediator_Calls#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Multiple_Errors#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Multiple_Errors.verified.txt │ │ ├── ReportingTests.Test_No_Messages_Program#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Notification_Without_Any_Handlers#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Notification_Without_Any_Handlers.verified.txt │ │ ├── ReportingTests.Test_Null_Namespace_Variable#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Request_Without_Handler_In_Referenced_Library#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Request_Without_Handler_In_Referenced_Library.verified.txt │ │ ├── ReportingTests.Test_Request_Without_Handler_Warning#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Request_Without_Handler_Warning.verified.txt │ │ ├── ReportingTests.Test_Static_Nested_Handler_Program#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Unassigned_Lifetime_Variable_In_Config#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Unassigned_Lifetime_Variable_In_Config.verified.txt │ │ ├── ReportingTests.Test_Unassigned_Namespace_Variable_In_Config#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Unassigned_Namespace_Variable_In_Config.verified.txt │ │ ├── ReportingTests.Test_Unassigned_Variables_In_Config#Mediator.g.verified.cs │ │ ├── ReportingTests.Test_Unassigned_Variables_In_Config.verified.txt │ │ ├── SampleTests.Test_ASPNET_Core_CleanArchitecture_Sample#Mediator.g.verified.cs │ │ ├── SampleTests.Test_ASPNET_Core_Indirect_Sample#Mediator.g.verified.cs │ │ ├── SampleTests.Test_Console#Mediator.g.verified.cs │ │ ├── SampleTests.Test_ConsoleAOT#Mediator.g.verified.cs │ │ ├── SampleTests.Test_InternalMessages_Sample#Mediator.g.verified.cs │ │ ├── SampleTests.Test_Notifications#Mediator.g.verified.cs │ │ ├── SampleTests.Test_Showcase#Mediator.g.verified.cs │ │ └── SampleTests.Test_Streaming#Mediator.g.verified.cs │ └── resources │ │ ├── AbstractHandlerProgram.cs │ │ ├── ByteArrayResponseProgram.cs │ │ ├── ConfigurationConflictProgram.cs │ │ ├── ConsoleProgram.cs │ │ ├── ConstVariablesConfig.cs │ │ ├── DeepNamespaceProgram.cs │ │ ├── DuplicateHandlersProgram.cs │ │ ├── IntCastLifetime.cs │ │ ├── InvalidVariablesConfig.cs │ │ ├── LocalLiteralVariableConfig.cs │ │ ├── LocalVariablesReferencingConstsConfig.cs │ │ ├── MultipleAddMediatorCalls.cs │ │ ├── MultipleErrorsProgram.cs │ │ ├── NoMessagesProgram.cs │ │ ├── NotificationWithoutHandlerProgram.cs │ │ ├── NullNamespaceVariable.cs │ │ ├── RequestWithoutHandlerProgram.cs │ │ ├── StaticNestedHandlerProgram.cs │ │ ├── StreamingProgram.cs │ │ ├── StructHandlerProgram.cs │ │ ├── UnassignedLifetimeVariableConfig.cs │ │ ├── UnassignedNamespaceVariableConfig.cs │ │ └── UnassignedVariablesConfig.cs └── Mediator.Tests │ ├── AbstractRequestHandlerTests.cs │ ├── Assertions.cs │ ├── BasicHandlerTests.cs │ ├── ConfigurationOutput.cs │ ├── CustomContainerTests.cs │ ├── Fixture.cs │ ├── HandlerInjectsMediatorTests.cs │ ├── Mediator.Tests.csproj │ ├── MultipleHandlersTests.cs │ ├── NonParallelCollectionDefinitionClass.cs │ ├── NonSyncNotificationHandlerTests.cs │ ├── NonSyncNotificationHandlerWithExceptionTests.cs │ ├── NotificationHandlersCollectionTests.cs │ ├── NullableResponseTests.cs │ ├── OpenConstrainedGenericsTests.cs │ ├── Pipeline │ ├── CommandSpecificPipeline.cs │ ├── ExceptionHandling │ │ ├── ConcreteExceptionHandlerTests.cs │ │ ├── GeneralHandlerTests.cs │ │ ├── MultipleHandlerTests.cs │ │ └── OnlyLoggingHandlerTests.cs │ ├── GenericPipeline.cs │ ├── IPipelineTestData.cs │ ├── PipelineTests.cs │ ├── PostProcessing │ │ ├── BasicTests.cs │ │ ├── ConcreteTests.cs │ │ └── GenericConstrainedTests.cs │ ├── PreProcessing │ │ ├── BasicTests.cs │ │ ├── ConcreteTests.cs │ │ └── GenericConstrainedTests.cs │ ├── SomePipeline.cs │ ├── SomeStreamingPipeline.cs │ └── StreamingPipelineTests.cs │ ├── PolymorphicDispatchTests.cs │ ├── RequestInheritanceTests.cs │ ├── ResponseInheritanceTests.cs │ ├── Scoped │ ├── Init.cs │ └── ScopedLifetimeTests.cs │ ├── SenderTests.cs │ ├── SingletonLifetimeTests.cs │ ├── SmokeTests.FastLazy.cs │ ├── SmokeTests.cs │ ├── StreamingTests.cs │ ├── StructResponseTests.cs │ ├── TestTypes │ ├── SomeCommand.cs │ ├── SomeCommandHandler.cs │ ├── SomeNotification.cs │ ├── SomeNotificationHandler.cs │ ├── SomeOtherNotificationHandler.cs │ ├── SomeQuery.cs │ ├── SomeQueryHandler.cs │ ├── SomeRequest.cs │ ├── SomeRequestHandler.cs │ ├── SomeResponse.cs │ ├── SomeStreamingCommand.cs │ ├── SomeStreamingCommandStruct.cs │ ├── SomeStreamingQuery.cs │ └── SomeStreamingRequest.cs │ └── Transient │ └── TransientLifetimeTests.cs └── version.json /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fixie.console": { 6 | "version": "3.2.0", 7 | "commands": [ 8 | "fixie" 9 | ], 10 | "rollForward": false 11 | }, 12 | "husky": { 13 | "version": "0.6.3", 14 | "commands": [ 15 | "husky" 16 | ], 17 | "rollForward": false 18 | }, 19 | "csharpier": { 20 | "version": "1.0.1", 21 | "commands": [ 22 | "csharpier" 23 | ], 24 | "rollForward": false 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /.csharpierignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | .git/ 4 | -------------------------------------------------------------------------------- /.csharpierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################### 2 | # Git Line Endings # 3 | ############################### 4 | 5 | # Set default behaviour to automatically normalize line endings. 6 | * text=auto eol=lf 7 | 8 | # Force batch scripts to always use CRLF line endings so that if a repo is accessed 9 | # in Windows via a file share from Linux, the scripts will work. 10 | *.{cmd,[cC][mM][dD]} text eol=crlf 11 | *.{bat,[bB][aA][tT]} text eol=crlf 12 | 13 | # Force bash scripts to always use LF line endings so that if a repo is accessed 14 | # in Unix via a file share from Windows, the scripts will work. 15 | *.sh text eol=lf 16 | 17 | *.jpg binary 18 | *.png binary 19 | *.gif binary 20 | 21 | *.cs text=auto diff=csharp 22 | 23 | *.csproj text=auto 24 | *.vbproj text=auto 25 | *.fsproj text=auto 26 | *.dbproj text=auto 27 | *.sln text=auto eol=crlf 28 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck source=/dev/null 3 | . "$(dirname "$0")/_/husky.sh" 4 | 5 | dotnet husky run -------------------------------------------------------------------------------- /.husky/task-runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "name": "Run csharpier", 5 | "command": "dotnet", 6 | "args": ["csharpier", "check", "${staged}"], 7 | "include": ["**/*.cs"] 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "xoofx.scriban", 4 | "ms-dotnettools.csdevkit", 5 | "editorconfig.editorconfig", 6 | "github.vscode-github-actions" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // Use IntelliSense to find out which attributes exist for C# debugging 6 | // Use hover for the description of the existing attributes 7 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/test/Mediator.Tests/bin/Debug/net5.0/Mediator.Tests.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/test/Mediator.Tests", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet.defaultSolution": "Mediator.sln" 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | latest 4 | enable 5 | true 6 | true 7 | true 8 | latest 9 | strict 10 | enable 11 | 12 | false 13 | https://github.com/martinothamar/Mediator 14 | https://github.com/martinothamar/Mediator 15 | mediator mediator-pattern source-generation source-gen source-generator sourcegenerator C# .NET dotnet 16 | git 17 | Martin Othamar 18 | Copyright 2022 Martin Othamar 19 | 20 | 21 | 22 | 8.0.0 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Martin Othamar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /benchmarks/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %(RecursiveDir)%(Filename)%(Extension) 7 | 8 | 9 | 10 | 11 | true 12 | true 13 | 14 | 15 | 16 | $(DefineConstants);$(ExtraDefineConstants) 17 | 18 | 19 | -------------------------------------------------------------------------------- /benchmarks/Justfile: -------------------------------------------------------------------------------- 1 | alias b := bench 2 | alias bq := bench-quick 3 | 4 | bench: 5 | dotnet run -c Release --project Mediator.Benchmarks/ 6 | 7 | bench-quick: 8 | dotnet run -c Release --project Mediator.Benchmarks/ -- --warmupCount 4 --iterationCount 4 --iterationTime 150 9 | -------------------------------------------------------------------------------- /benchmarks/Mediator.Benchmarks/CustomColumn.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | namespace Mediator.Benchmarks; 4 | 5 | public sealed class CustomColumn : IColumn 6 | { 7 | private readonly Func _getTag; 8 | 9 | public string Id { get; } 10 | public string ColumnName { get; } 11 | 12 | public CustomColumn(string columnName, Func getTag) 13 | { 14 | _getTag = getTag; 15 | ColumnName = columnName; 16 | Id = nameof(CustomColumn) + "." + ColumnName; 17 | } 18 | 19 | public bool IsDefault(Summary summary, BenchmarkCase benchmarkCase) => false; 20 | 21 | public string GetValue(Summary summary, BenchmarkCase benchmarkCase) => _getTag(summary, benchmarkCase); 22 | 23 | public bool IsAvailable(Summary summary) => true; 24 | 25 | public bool AlwaysShow => true; 26 | public ColumnCategory Category => ColumnCategory.Params; 27 | public int PriorityInCategory => 0; 28 | public bool IsNumeric => false; 29 | public UnitType UnitType => UnitType.Dimensionless; 30 | public string Legend => $"Custom '{ColumnName}' tag column"; 31 | 32 | public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style) => 33 | GetValue(summary, benchmarkCase); 34 | 35 | public override string ToString() => ColumnName; 36 | } 37 | -------------------------------------------------------------------------------- /benchmarks/Mediator.Benchmarks/Internal/Unboxing.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Mediator.Benchmarks.Notifications; 3 | 4 | namespace Mediator.Benchmarks.Internal; 5 | 6 | [MemoryDiagnoser] 7 | [Orderer(SummaryOrderPolicy.FastestToSlowest, MethodOrderPolicy.Declared)] 8 | [RankColumn] 9 | public class UnboxingBenchmarks 10 | { 11 | private object _handler; 12 | private SingleHandlerNotification _notification; 13 | 14 | [GlobalSetup] 15 | public void Setup() 16 | { 17 | _handler = new SingleHandler(); 18 | _notification = new SingleHandlerNotification(Guid.NewGuid()); 19 | } 20 | 21 | [Benchmark] 22 | public ValueTask Cast() 23 | { 24 | var handler = (INotificationHandler)_handler; 25 | return handler.Handle(_notification, default); 26 | } 27 | 28 | [Benchmark] 29 | public ValueTask Patternmatch() 30 | { 31 | if (_handler is not INotificationHandler handler) 32 | return default; 33 | return handler.Handle(_notification, default); 34 | } 35 | 36 | [Benchmark] 37 | public ValueTask UnsafeAs() 38 | { 39 | var handler = Unsafe.As>(_handler); 40 | return handler.Handle(_notification, default); 41 | } 42 | 43 | [Benchmark(Baseline = true)] 44 | public ValueTask UnsafeAsReinterpret() 45 | { 46 | var handler = Unsafe.As>(ref _handler); 47 | return handler.Handle(_notification, default); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /benchmarks/Mediator.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | ConsoleLogger.Default.WriteLine($"Running with lifetime: Impl={Mediator.Mediator.ServiceLifetime}"); 4 | ConsoleLogger.Default.WriteLine(); 5 | 6 | // BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); 7 | 8 | BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); 9 | -------------------------------------------------------------------------------- /common/Options.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | [assembly: MediatorOptions( 5 | #if Mediator_Lifetime_Singleton 6 | ServiceLifetime = ServiceLifetime.Singleton 7 | #elif Mediator_Lifetime_Transient 8 | ServiceLifetime = ServiceLifetime.Transient 9 | #elif Mediator_Lifetime_Scoped 10 | ServiceLifetime = ServiceLifetime.Scoped 11 | #else 12 | ServiceLifetime = ServiceLifetime.Singleton 13 | #endif 14 | #if Mediator_Publisher_TaskWhenAll 15 | , 16 | NotificationPublisherType = typeof(TaskWhenAllPublisher) 17 | #elif Mediator_Publisher_ForeachAwait 18 | , 19 | NotificationPublisherType = typeof(ForeachAwaitPublisher) 20 | #else 21 | , 22 | NotificationPublisherType = typeof(ForeachAwaitPublisher) 23 | #endif 24 | )] 25 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.200", 4 | "rollForward": "latestFeature", 5 | "allowPrerelease": false 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /img/benchmarks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinothamar/Mediator/c4a9e0ffa4b371de1c34ee2a2fbd249f42ef2fdc/img/benchmarks.png -------------------------------------------------------------------------------- /img/missing_request_handler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinothamar/Mediator/c4a9e0ffa4b371de1c34ee2a2fbd249f42ef2fdc/img/missing_request_handler.png -------------------------------------------------------------------------------- /img/multiple_request_handlers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinothamar/Mediator/c4a9e0ffa4b371de1c34ee2a2fbd249f42ef2fdc/img/multiple_request_handlers.png -------------------------------------------------------------------------------- /samples/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | all 7 | runtime; build; native; contentfiles; analyzers 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /samples/Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | ## Samples 2 | 3 | This directory contains various samples of Mediator usage 4 | 5 | * [basic/](/samples/basic/) shows basic usage of the primitives - requests, notifications, pipelinebehaviors 6 | * [apps/](/samples/apps/) shows more full-featured usage in APIs/applications using various architectures, such as Clean Architecture 7 | * [use-cases/](/samples/use-cases/) shows more specific usecases and patterns, such as Autofac, query caching pattern 8 | 9 | In [Showcase/](/samples/Showcase/) you will find the code for what is shown in the root README. 10 | -------------------------------------------------------------------------------- /samples/Showcase/README.md: -------------------------------------------------------------------------------- 1 | ## SimpleEndToEnd 2 | 3 | This is the same code as in the root [README](/) end to end example. 4 | It is meant to illustrate the various ways the Mediator can be used. 5 | 6 | ### Build and run 7 | 8 | ```console 9 | $ dotnet run 10 | Got the right ID: (Ping { Id = 34dd8a5f-9ed5-4bde-b675-188155b9ace1 }, Pong { Id = 34dd8a5f-9ed5-4bde-b675-188155b9ace1 }) 11 | Error handling message: Invalid input 12 | Done! 13 | ``` 14 | -------------------------------------------------------------------------------- /samples/Showcase/Showcase.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | disable 6 | 7 | 8 | true 9 | 10 | Generated 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor.Client/AspNetCoreBlazor.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | true 7 | Default 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 28 | 29 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor.Client/Pages/Counter.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | 3 | namespace AspNetCoreBlazor.Client.Pages; 4 | 5 | public sealed record IncrementCounter() : IRequest; 6 | 7 | public sealed class IncrementCounterHandler : IRequestHandler 8 | { 9 | private long _counter = 0; 10 | 11 | public long Current => Interlocked.Read(ref _counter); 12 | 13 | public ValueTask Handle(IncrementCounter request, CancellationToken cancellationToken) 14 | { 15 | var count = Interlocked.Increment(ref _counter); 16 | return new ValueTask(count); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | @using Mediator 3 | @rendermode InteractiveAuto 4 | @inject IMediator Mediator 5 | 6 | Counter 7 | 8 |

Counter

9 | 10 |

Current count: @currentCount

11 | 12 | 13 | 14 | @code { 15 | private long currentCount = 0; 16 | 17 | private async ValueTask IncrementCount() 18 | { 19 | currentCount = await Mediator.Send(new IncrementCounter()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor.Client/Program.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreBlazor.Client.Pages; 2 | using Mediator; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | 5 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 6 | 7 | builder.Services.AddMediator( 8 | (MediatorOptions options) => 9 | { 10 | options.Assemblies = [typeof(IncrementCounter)]; 11 | options.GenerateTypesAsInternal = true; 12 | } 13 | ); 14 | 15 | await builder.Build().RunAsync(); 16 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using AspNetCoreBlazor.Client 10 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/AspNetCoreBlazor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 27 | 28 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/Layout/NavMenu.razor: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 30 | 31 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/Pages/Error.razor: -------------------------------------------------------------------------------- 1 | @page "/Error" 2 | @using System.Diagnostics 3 | 4 | Error 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (ShowRequestId) 10 | { 11 |

12 | Request ID: @RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | 27 | @code{ 28 | [CascadingParameter] 29 | private HttpContext? HttpContext { get; set; } 30 | 31 | private string? RequestId { get; set; } 32 | private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 33 | 34 | protected override void OnInitialized() => 35 | RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; 36 | } 37 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/Pages/Weather.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Mediator; 3 | 4 | namespace AspNetCoreBlazor.Components.Pages; 5 | 6 | internal sealed record GetWeatherForecasts(int Count) : IStreamQuery; 7 | 8 | internal sealed class WeatherForecast 9 | { 10 | public DateOnly Date { get; set; } 11 | public int TemperatureC { get; set; } 12 | public string? Summary { get; set; } 13 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 14 | } 15 | 16 | internal sealed class GetWeatherForecastHandler : IStreamQueryHandler 17 | { 18 | public async IAsyncEnumerable Handle( 19 | GetWeatherForecasts query, 20 | [EnumeratorCancellation] CancellationToken cancellationToken 21 | ) 22 | { 23 | var startDate = DateOnly.FromDateTime(DateTime.Now); 24 | var summaries = new[] 25 | { 26 | "Freezing", 27 | "Bracing", 28 | "Chilly", 29 | "Cool", 30 | "Mild", 31 | "Warm", 32 | "Balmy", 33 | "Hot", 34 | "Sweltering", 35 | "Scorching", 36 | }; 37 | 38 | for (var i = 0; i < query.Count; i++) 39 | { 40 | await Task.Delay(100, cancellationToken); 41 | 42 | yield return new WeatherForecast 43 | { 44 | Date = startDate.AddDays(i), 45 | TemperatureC = Random.Shared.Next(-20, 55), 46 | Summary = summaries[Random.Shared.Next(summaries.Length)], 47 | }; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/Pages/Weather.razor: -------------------------------------------------------------------------------- 1 | @page "/weather" 2 | @attribute [StreamRendering] 3 | @using Mediator 4 | @inject IMediator Mediator 5 | 6 | Weather 7 | 8 |

Weather

9 | 10 |

This component demonstrates showing data.

11 | 12 | @if (forecasts == null) 13 | { 14 |

Loading...

15 | } 16 | else 17 | { 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | @foreach (var forecast in forecasts) 29 | { 30 | 31 | 32 | 33 | 34 | 35 | 36 | } 37 | 38 |
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
39 | } 40 | 41 | @code { 42 | private WeatherForecast[]? forecasts; 43 | 44 | protected override async Task OnInitializedAsync() 45 | { 46 | List forecasts = new(); 47 | await foreach (var forecast in Mediator.CreateStream(new GetWeatherForecasts(5))) 48 | { 49 | forecasts.Add(forecast); 50 | this.forecasts = forecasts.ToArray(); 51 | this.StateHasChanged(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using AspNetCoreBlazor 10 | @using AspNetCoreBlazor.Client 11 | @using AspNetCoreBlazor.Components 12 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Program.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreBlazor.Client.Pages; 2 | using AspNetCoreBlazor.Components; 3 | using AspNetCoreBlazor.Components.Pages; 4 | using Mediator; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | // Add services to the container. 9 | builder.Services.AddRazorComponents().AddInteractiveServerComponents().AddInteractiveWebAssemblyComponents(); 10 | 11 | builder.Services.AddMediator( 12 | (MediatorOptions options) => 13 | { 14 | options.Assemblies = [typeof(GetWeatherForecasts), typeof(IncrementCounter)]; 15 | options.GenerateTypesAsInternal = true; 16 | } 17 | ); 18 | 19 | var app = builder.Build(); 20 | 21 | // Configure the HTTP request pipeline. 22 | if (app.Environment.IsDevelopment()) 23 | { 24 | app.UseWebAssemblyDebugging(); 25 | } 26 | else 27 | { 28 | app.UseExceptionHandler("/Error", createScopeForErrors: true); 29 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 30 | app.UseHsts(); 31 | } 32 | 33 | app.UseHttpsRedirection(); 34 | 35 | app.UseStaticFiles(); 36 | app.UseAntiforgery(); 37 | 38 | app.MapRazorComponents() 39 | .AddInteractiveServerRenderMode() 40 | .AddInteractiveWebAssemblyRenderMode() 41 | .AddAdditionalAssemblies(typeof(AspNetCoreBlazor.Client._Imports).Assembly); 42 | 43 | app.Run(); 44 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "http": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 9 | "applicationUrl": "http://localhost:5000", 10 | "environmentVariables": { 11 | "ASPNETCORE_ENVIRONMENT": "Development" 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/martinothamar/Mediator/c4a9e0ffa4b371de1c34ee2a2fbd249f42ef2fdc/samples/apps/ASPNET_CORE_Blazor/AspNetCoreBlazor/wwwroot/favicon.png -------------------------------------------------------------------------------- /samples/apps/ASPNET_CORE_Blazor/README.md: -------------------------------------------------------------------------------- 1 | ## ASPNET Core Blazor app 2 | 3 | Scaffolded as `dotnet new blazor -int Auto`. 4 | 5 | > Uses interactive server-side rendering while downloading the Blazor bundle and activating the Blazor runtime on the client, then uses client-side rendering with WebAssembly. 6 | 7 | ## Run 8 | 9 | ```sh 10 | cd AspNetCoreBlazor 11 | dotnet run 12 | ``` 13 | 14 | Now you can open [localhost:5000](http://localhost:5000) and check out the 15 | * Counter page - it uses a Mediator request to bump the counter 16 | * Weather page - it uses a streaming query to update the UI 17 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core/ASPNET_Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | true 9 | 10 | Generated 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using ASPNETCore.WeatherForecasts; 2 | using Mediator; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace ASPNETCore.Controllers 6 | { 7 | [ApiController] 8 | [Route("[controller]")] 9 | public class WeatherForecastController : ControllerBase 10 | { 11 | private readonly ILogger _logger; 12 | private readonly IMediator _mediator; 13 | 14 | public WeatherForecastController(ILogger logger, IMediator mediator) 15 | { 16 | _logger = logger; 17 | _mediator = mediator; 18 | } 19 | 20 | [HttpGet(Name = "GetWeatherForecast")] 21 | public ValueTask> Get() 22 | { 23 | return _mediator.Send(new GetWeatherForecasts()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core/Program.cs: -------------------------------------------------------------------------------- 1 | using ASPNETCore.WeatherForecasts; 2 | using Mediator; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | // Add services to the container. 7 | 8 | builder.Services.AddControllers(); 9 | 10 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 11 | builder.Services.AddEndpointsApiExplorer(); 12 | builder.Services.AddSwaggerGen(); 13 | 14 | builder.Services.AddMediator( 15 | (MediatorOptions options) => 16 | { 17 | options.Assemblies = [typeof(GetWeatherForecasts).Assembly]; 18 | } 19 | ); 20 | 21 | var app = builder.Build(); 22 | 23 | // Configure the HTTP request pipeline. 24 | if (app.Environment.IsDevelopment()) 25 | { 26 | app.UseSwagger(); 27 | app.UseSwaggerUI(); 28 | } 29 | 30 | app.UseHttpsRedirection(); 31 | 32 | app.UseAuthorization(); 33 | 34 | app.MapControllers(); 35 | 36 | app.Run(); 37 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "ASPNETCore": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "launchUrl": "swagger", 9 | "applicationUrl": "http://localhost:5000", 10 | "environmentVariables": { 11 | "ASPNETCORE_ENVIRONMENT": "Development" 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core/WeatherForecasts/GetWeatherForecasts.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | 3 | namespace ASPNETCore.WeatherForecasts 4 | { 5 | public sealed record GetWeatherForecasts : IQuery>; 6 | 7 | public sealed record GetWeatherForecastsHandler : IQueryHandler> 8 | { 9 | private static readonly string[] Summaries = new[] 10 | { 11 | "Freezing", 12 | "Bracing", 13 | "Chilly", 14 | "Cool", 15 | "Mild", 16 | "Warm", 17 | "Balmy", 18 | "Hot", 19 | "Sweltering", 20 | "Scorching", 21 | }; 22 | 23 | public ValueTask> Handle( 24 | GetWeatherForecasts query, 25 | CancellationToken cancellationToken 26 | ) 27 | { 28 | var result = Enumerable 29 | .Range(1, 5) 30 | .Select(index => new WeatherForecast 31 | { 32 | Date = DateTime.Now.AddDays(index), 33 | TemperatureC = Random.Shared.Next(-20, 55), 34 | Summary = Summaries[Random.Shared.Next(Summaries.Length)], 35 | }) 36 | .ToArray(); 37 | 38 | return new ValueTask>(result); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core/WeatherForecasts/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | namespace ASPNETCore 2 | { 3 | public class WeatherForecast 4 | { 5 | public DateTime Date { get; set; } 6 | 7 | public int TemperatureC { get; set; } 8 | 9 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 10 | 11 | public string? Summary { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Api/AspNetCoreSample.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreSample.Api; 2 | using AspNetCoreSample.Application; 3 | using AspNetCoreSample.Infrastructure; 4 | 5 | var builder = WebApplication.CreateBuilder(args); 6 | 7 | builder.Services.AddApplication(); 8 | builder.Services.AddInfrastructure(); 9 | 10 | builder.Services.AddEndpointsApiExplorer(); 11 | builder.Services.AddSwaggerGen(); 12 | 13 | var app = builder.Build(); 14 | 15 | app.UseSwagger(); 16 | app.UseSwaggerUI(); 17 | 18 | app.MapTodoApi(); 19 | 20 | app.Run(); 21 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "AspNetCoreSample.Api": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "launchBrowser": true, 7 | "launchUrl": "swagger", 8 | "applicationUrl": "http://localhost:5000", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Api/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/IValidate.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using Mediator; 3 | 4 | namespace AspNetCoreSample.Application; 5 | 6 | public interface IValidate : IMessage 7 | { 8 | bool IsValid([NotNullWhen(false)] out ValidationError? error); 9 | } 10 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/Pipeline/ErrorLoggingBehaviour.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace AspNetCoreSample.Application; 5 | 6 | public sealed class ErrorLoggingBehaviour : MessageExceptionHandler 7 | where TMessage : notnull, IMessage 8 | { 9 | private readonly ILogger> _logger; 10 | 11 | public ErrorLoggingBehaviour(ILogger> logger) 12 | { 13 | _logger = logger; 14 | } 15 | 16 | protected override ValueTask> Handle( 17 | TMessage message, 18 | Exception exception, 19 | CancellationToken cancellationToken 20 | ) 21 | { 22 | _logger.LogError(exception, "Error handling message of type {messageType}", message.GetType().Name); 23 | return NotHandled; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/Pipeline/MessageValidatorBehaviour.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | 3 | namespace AspNetCoreSample.Application; 4 | 5 | public sealed class MessageValidatorBehaviour : MessagePreProcessor 6 | where TMessage : IValidate 7 | { 8 | protected override ValueTask Handle(TMessage message, CancellationToken cancellationToken) 9 | { 10 | if (!message.IsValid(out var validationError)) 11 | throw new ValidationException(validationError); 12 | 13 | return default; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace AspNetCoreSample.Application; 5 | 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddApplication(this IServiceCollection services) 9 | { 10 | services.AddMediator( 11 | (MediatorOptions options) => 12 | { 13 | options.Assemblies = [typeof(AddTodoItem)]; 14 | options.ServiceLifetime = ServiceLifetime.Scoped; 15 | } 16 | ); 17 | return services 18 | .AddSingleton(typeof(IPipelineBehavior<,>), typeof(ErrorLoggingBehaviour<,>)) 19 | .AddSingleton(typeof(IPipelineBehavior<,>), typeof(MessageValidatorBehaviour<,>)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/Todos/AddTodoItem.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using AspNetCoreSample.Domain; 3 | using FluentValidation; 4 | using Mediator; 5 | 6 | namespace AspNetCoreSample.Application; 7 | 8 | public class AddTodoItemValidator : AbstractValidator 9 | { 10 | public AddTodoItemValidator() 11 | { 12 | RuleFor(x => x.Title).Length(1, 40); 13 | RuleFor(x => x.Text).Length(1, 150); 14 | } 15 | } 16 | 17 | public sealed record AddTodoItem(string Title, string Text) : ICommand, IValidate 18 | { 19 | public bool IsValid([NotNullWhen(false)] out ValidationError? error) 20 | { 21 | var validator = new AddTodoItemValidator(); 22 | var result = validator.Validate(this); 23 | if (result.IsValid) 24 | error = null; 25 | else 26 | error = new ValidationError(result.Errors.Select(e => e.ErrorMessage).ToArray()); 27 | 28 | return result.IsValid; 29 | } 30 | } 31 | 32 | public sealed class TodoItemCommandHandler : ICommandHandler 33 | { 34 | private readonly ITodoItemRepository _repository; 35 | 36 | public TodoItemCommandHandler(ITodoItemRepository repository) => _repository = repository; 37 | 38 | public async ValueTask Handle(AddTodoItem command, CancellationToken cancellationToken) 39 | { 40 | var item = new TodoItem(Guid.NewGuid(), command.Title, command.Text, false); 41 | 42 | await _repository.AddItem(item, cancellationToken); 43 | 44 | return item; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/Todos/GetTodoItems.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreSample.Domain; 2 | using Mediator; 3 | 4 | namespace AspNetCoreSample.Application; 5 | 6 | public sealed record GetTodoItems() : IQuery>; 7 | 8 | public sealed class TodoItemQueryHandler : IQueryHandler> 9 | { 10 | private readonly ITodoItemRepository _repository; 11 | 12 | public TodoItemQueryHandler(ITodoItemRepository repository) => _repository = repository; 13 | 14 | public ValueTask> Handle(GetTodoItems query, CancellationToken cancellationToken) => 15 | _repository.GetItems(cancellationToken); 16 | } 17 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/Todos/ITodoItemRepository.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreSample.Domain; 2 | 3 | namespace AspNetCoreSample.Application; 4 | 5 | public interface ITodoItemRepository 6 | { 7 | ValueTask> GetItems(CancellationToken cancellationToken); 8 | 9 | ValueTask AddItem(TodoItem item, CancellationToken cancellationToken); 10 | } 11 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/ValidationError.cs: -------------------------------------------------------------------------------- 1 | namespace AspNetCoreSample.Application; 2 | 3 | public sealed record ValidationError(IEnumerable Errors); 4 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Application/ValidationException.cs: -------------------------------------------------------------------------------- 1 | namespace AspNetCoreSample.Application; 2 | 3 | public sealed class ValidationException : Exception 4 | { 5 | public ValidationException(ValidationError validationError) 6 | : base("Validation error") => ValidationError = validationError; 7 | 8 | public ValidationError ValidationError { get; } 9 | } 10 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Domain/AspNetCoreSample.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Domain/TodoItem.cs: -------------------------------------------------------------------------------- 1 | namespace AspNetCoreSample.Domain; 2 | 3 | public sealed record TodoItem(Guid Id, string Title, string Text, bool Done); 4 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Infrastructure/AspNetCoreSample.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Infrastructure/InMemoryTodoItemRepository.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreSample.Application; 2 | using AspNetCoreSample.Domain; 3 | 4 | namespace AspNetCoreSample.Infrastructure; 5 | 6 | internal sealed class InMemoryTodoItemRepository : ITodoItemRepository 7 | { 8 | private readonly object _lock = new { }; 9 | private readonly List _db = new(); 10 | 11 | public ValueTask AddItem(TodoItem item, CancellationToken cancellationToken) 12 | { 13 | lock (_lock) 14 | { 15 | if (_db.Any(i => i.Id == item.Id)) 16 | throw new Exception("Item already exists"); 17 | 18 | _db.Add(item); 19 | } 20 | 21 | return default; 22 | } 23 | 24 | public ValueTask> GetItems(CancellationToken cancellationToken) 25 | { 26 | lock (_lock) 27 | { 28 | return new ValueTask>(_db.ToArray()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/AspNetCoreSample.Infrastructure/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreSample.Application; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace AspNetCoreSample.Infrastructure; 5 | 6 | public static class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddInfrastructure(this IServiceCollection services) 9 | { 10 | return services.AddSingleton(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/README.md: -------------------------------------------------------------------------------- 1 | ## Clean architecture with ASP.NET Core 2 | 3 | This sample shows a more complete example on how Mediator can be used, following 4 | an onion/clean architecture style. 5 | 6 | ### Run 7 | 8 | Run the application in Visual Studio or through the dotnet CLI. 9 | The Swagger UI should be visible at [http://localhost:5000/swagger/index.html](http://localhost:5000/swagger/index.html). 10 | 11 | #### REST client 12 | 13 | Use the `post-todo.http` file and the "REST Client" VSCode extension to test it out: 14 | 15 | ```http 16 | POST http://localhost:5000/api/todos HTTP/1.1 17 | content-type: application/json 18 | 19 | { 20 | "title": "", 21 | "text": "This is a todo without a title, we should get an error..." 22 | } 23 | 24 | HTTP/1.1 400 Bad Request 25 | Connection: close 26 | Date: Mon, 17 May 2021 10:51:45 GMT 27 | Content-Type: application/json; charset=utf-8 28 | Server: Kestrel 29 | Transfer-Encoding: chunked 30 | 31 | { 32 | "errors": [ 33 | "'Title' must be between 1 and 40 characters. You entered 0 characters." 34 | ] 35 | } 36 | ``` 37 | 38 | ### Code smells 39 | 40 | #### Exceptions for control flow 41 | 42 | Exceptions are not good for control flow, but in this sample a `ValidationException` 43 | is used to "return early" from a generic pipeline behaviour. 44 | The `MessageValidatorBehaviour` will process any message sent through mediator which implements `IValidate`. 45 | 46 | Ideally, the return value for the `AddTodoItem` was some sort of `Result` or `OneOf` type, 47 | in which case we could just return an instance of `ValidationError` in the pipeline behaviour before invoking the handler. 48 | Then the caller would have to handle both success and failure cases, `TResponse` or `ValidationError` and return statuscode accordingly. 49 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_CleanArchitecture/post-todo.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:5000/api/TodoItems HTTP/1.1 2 | content-type: application/json 3 | 4 | { 5 | "title": "", 6 | "text": "This is a todo without a title, we should get an error..." 7 | } -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Application/Program.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreIndirect.Application; 2 | using Mediator; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | // Add services to the container. 7 | 8 | builder.Services.AddControllers(); 9 | 10 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 11 | builder.Services.AddEndpointsApiExplorer(); 12 | builder.Services.AddSwaggerGen(); 13 | 14 | builder.Services.AddMediator( 15 | (MediatorOptions options) => 16 | { 17 | options.Assemblies = [typeof(GetWeatherForecast)]; 18 | } 19 | ); 20 | 21 | var app = builder.Build(); 22 | 23 | // Configure the HTTP request pipeline. 24 | if (app.Environment.IsDevelopment()) 25 | { 26 | app.UseSwagger(); 27 | app.UseSwaggerUI(); 28 | } 29 | 30 | app.UseHttpsRedirection(); 31 | 32 | app.UseAuthorization(); 33 | 34 | app.MapControllers(); 35 | 36 | app.Run(); 37 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Application/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "AspNetCoreIndirect.Application": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "launchBrowser": true, 7 | "launchUrl": "swagger", 8 | "applicationUrl": "http://localhost:5000", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Application/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Application/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.BaseClasses/ApplicationController.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace AspNetCoreIndirect.BaseClasses; 7 | 8 | [ApiController] 9 | public abstract class ApplicationController : ControllerBase 10 | { 11 | /// 12 | /// Send request through Mediator and convert result to IActionResult. 13 | /// 14 | /// request to process. 15 | /// type of the request. 16 | /// type of the response. 17 | /// Action result. 18 | protected async Task ProcessAsync(TRequest request) 19 | where TRequest : IRequest 20 | { 21 | try 22 | { 23 | var mediator = HttpContext.RequestServices.GetRequiredService(); 24 | 25 | var result = await mediator.Send(request, HttpContext.RequestAborted); 26 | 27 | if (typeof(TResponse) == typeof(Unit)) 28 | { 29 | return NoContent(); 30 | } 31 | 32 | return Ok(result); 33 | } 34 | catch (Exception ex) 35 | { 36 | return new ObjectResult(new { ex.Message }) { StatusCode = StatusCodes.Status500InternalServerError }; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.BaseClasses/ApplicationHandler.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | 3 | namespace AspNetCoreIndirect.BaseClasses; 4 | 5 | public abstract class ApplicationHandler : IRequestHandler 6 | where TRequest : IRequest 7 | { 8 | public ValueTask Handle(TRequest request, CancellationToken cancellationToken) 9 | { 10 | // For the sake of simplicity we'll just return downstream implementation 11 | return ProcessRequest(request, cancellationToken); 12 | } 13 | 14 | protected abstract ValueTask ProcessRequest(TRequest request, CancellationToken cancellationToken); 15 | } 16 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.BaseClasses/ApplicationRequest.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | 3 | namespace AspNetCoreIndirect.BaseClasses; 4 | 5 | public abstract record ApplicationRequest : IRequest; 6 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.BaseClasses/AspNetCoreIndirect.BaseClasses.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Implementations/AspNetCoreIndirect.Implementations.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Implementations/GetWeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreIndirect.BaseClasses; 2 | 3 | namespace AspNetCoreIndirect.Application; 4 | 5 | public record GetWeatherForecast(int Count = 5) : ApplicationRequest>; 6 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Implementations/GetWeatherForecastHandler.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreIndirect.BaseClasses; 2 | 3 | namespace AspNetCoreIndirect.Application.Controllers; 4 | 5 | public class GetWeatherForecastHandler : ApplicationHandler> 6 | { 7 | private static readonly string[] _summaries = 8 | { 9 | "Freezing", 10 | "Bracing", 11 | "Chilly", 12 | "Cool", 13 | "Mild", 14 | "Warm", 15 | "Balmy", 16 | "Hot", 17 | "Sweltering", 18 | "Scorching", 19 | }; 20 | 21 | protected override ValueTask> ProcessRequest( 22 | GetWeatherForecast request, 23 | CancellationToken cancellationToken 24 | ) 25 | { 26 | IReadOnlyList result = Enumerable 27 | .Range(1, request.Count) 28 | .Select(index => new WeatherForecast 29 | { 30 | Date = DateTime.Now.AddDays(index), 31 | TemperatureC = Random.Shared.Next(-20, 55), 32 | Summary = _summaries[Random.Shared.Next(_summaries.Length)], 33 | }) 34 | .ToArray(); 35 | 36 | return ValueTask.FromResult(result); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Implementations/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | namespace AspNetCoreIndirect.Application; 2 | 3 | public class WeatherForecast 4 | { 5 | public DateTime Date { get; set; } 6 | 7 | public int TemperatureC { get; set; } 8 | 9 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 10 | 11 | public string? Summary { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/AspNetCoreIndirect.Implementations/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreIndirect.BaseClasses; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace AspNetCoreIndirect.Application.Controllers; 5 | 6 | [Route("[controller]")] 7 | public class WeatherForecastController : ApplicationController 8 | { 9 | [HttpGet] 10 | public Task Get([FromQuery] GetWeatherForecast request) => 11 | ProcessAsync>(request); 12 | } 13 | -------------------------------------------------------------------------------- /samples/apps/ASPNET_Core_Indirect/get-weather-forecast.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:5000/WeatherForecast 2 | -------------------------------------------------------------------------------- /samples/apps/InternalMessages/InternalMessages.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using InternalMessages.Application; 2 | using InternalMessages.Domain; 3 | using Mediator; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | var builder = WebApplication.CreateBuilder(args); 7 | 8 | builder.Services.AddMediator( 9 | (MediatorOptions options) => 10 | { 11 | options.Assemblies = [typeof(Ping), typeof(PingHandler)]; 12 | } 13 | ); 14 | 15 | var app = builder.Build(); 16 | 17 | app.MapGet( 18 | "/", 19 | async ([FromServices] Mediator.Mediator mediator) => 20 | { 21 | var request = new Ping(Guid.NewGuid()); 22 | 23 | var response = await mediator.Send(request); 24 | 25 | return Results.Ok(response.Id); 26 | } 27 | ); 28 | 29 | app.Run(); 30 | -------------------------------------------------------------------------------- /samples/apps/InternalMessages/InternalMessages.Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "InternalMessages.Api": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "launchBrowser": true, 7 | "applicationUrl": "http://localhost:5000", 8 | "environmentVariables": { 9 | "ASPNETCORE_ENVIRONMENT": "Development" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/apps/InternalMessages/InternalMessages.Api/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/apps/InternalMessages/InternalMessages.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /samples/apps/InternalMessages/InternalMessages.Application/InternalMessages.Application.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /samples/apps/InternalMessages/InternalMessages.Application/PingHandler.cs: -------------------------------------------------------------------------------- 1 | using InternalMessages.Domain; 2 | using Mediator; 3 | 4 | namespace InternalMessages.Application; 5 | 6 | internal sealed class PingHandler : IRequestHandler 7 | { 8 | private readonly IMediator _mediator; 9 | 10 | public PingHandler(IMediator mediator) => _mediator = mediator; 11 | 12 | public async ValueTask Handle(Ping request, CancellationToken cancellationToken) 13 | { 14 | await _mediator.Publish(new PingPonged(request.Id), cancellationToken); 15 | return new Pong(request.Id); 16 | } 17 | } 18 | 19 | internal sealed class PingPongedHandler : INotificationHandler 20 | { 21 | public ValueTask Handle(PingPonged notification, CancellationToken cancellationToken) 22 | { 23 | Console.WriteLine($"PingPonged: {notification.Id}"); 24 | return default; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /samples/apps/InternalMessages/InternalMessages.Domain/InternalMessages.Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /samples/apps/InternalMessages/InternalMessages.Domain/Ping.cs: -------------------------------------------------------------------------------- 1 | using Mediator; 2 | 3 | namespace InternalMessages.Domain; 4 | 5 | internal sealed record Ping(Guid Id) : IRequest; 6 | 7 | internal sealed record Pong(Guid Id); 8 | 9 | internal sealed record PingPonged(Guid Id) : INotification; 10 | -------------------------------------------------------------------------------- /samples/basic/Console/Console.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | disable 6 | 7 | 8 | true 9 | 10 | Generated 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /samples/basic/Console/README.md: -------------------------------------------------------------------------------- 1 | ## SimpleConsole 2 | 3 | Simple showcase of Mediator. 4 | 5 | ### Build and run 6 | 7 | ```console 8 | $ dotnet run 9 | 1) Running logger handler 10 | 2) Running ping validator 11 | 3) Valid input! 12 | 4) Returning pong! 13 | 5) No error! 14 | ----------------------------------- 15 | ID: 4f5e8fe3-e64f-4042-9ed3-33b894be8776 16 | Ping { Id = 4f5e8fe3-e64f-4042-9ed3-33b894be8776 } 17 | Pong { Id = 4f5e8fe3-e64f-4042-9ed3-33b894be8776 } 18 | ``` 19 | -------------------------------------------------------------------------------- /samples/basic/NetFramework/Console/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /samples/basic/NetFramework/Console/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Console")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Console")] 13 | [assembly: AssemblyCopyright("Copyright © 2025")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7cbe34be-e223-4c4a-b0d7-19029535c3c8")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /samples/basic/NetFramework/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /samples/basic/NotificationPublisher/README.md: -------------------------------------------------------------------------------- 1 | ## NotificationPublisher 2 | 3 | Simple showcase of using a custom notification publisher, by implementing `INotificationPublisher`. 4 | The custom publisher catches all exceptions and logs them, a so called fire-and-forget implementation. 5 | 6 | ### Build and run 7 | 8 | ```console 9 | $ dotnet run 10 | Publishing! 11 | ----------------------------------- 12 | MyNotificationHandler - 6ae7d56b-8a2f-404c-a24b-c5df1e6691d2 13 | System.Exception: Something went wrong! 14 | at MyNotificationHandler.Handle(Notification notification, CancellationToken cancellationToken) in /home/martin/code/private/Mediator/samples/basic/NotificationPublisher/Program.cs:line 79 15 | at MyNotificationPublisher.Publish[TNotification](NotificationHandlers`1 handlers, TNotification notification, CancellationToken cancellationToken) in /home/martin/code/private/Mediator/samples/basic/NotificationPublisher/Program.cs:line 46 16 | ----------------------------------- 17 | Finished publishing! 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /samples/basic/Notifications/Notifications.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | disable 6 | 7 | 8 | true 9 | 10 | Generated 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /samples/basic/Notifications/README.md: -------------------------------------------------------------------------------- 1 | ## Notifications 2 | 3 | Simple showcase of using notifications and various notification-handlers. 4 | 5 | ### Build and run 6 | 7 | ```console 8 | $ dotnet run 9 | Publishing! 10 | ----------------------------------- 11 | CatchAllNotificationHandler - 360412c0-e7cf-452c-97f3-0e4be166b26c 12 | ConcreteNotificationHandler - 360412c0-e7cf-452c-97f3-0e4be166b26c 13 | GenericNotificationHandler`1 - 360412c0-e7cf-452c-97f3-0e4be166b26c 14 | ----------------------------------- 15 | Finished publishing! 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /samples/basic/Streaming/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Mediator; 7 | using Microsoft.Extensions.DependencyInjection; 8 | 9 | var services = new ServiceCollection(); 10 | 11 | // This extensions method is generated, and is put in the "Mediator" namespace by default. 12 | services.AddMediator( 13 | (MediatorOptions options) => 14 | { 15 | options.Assemblies = [typeof(StreamPing)]; 16 | } 17 | ); 18 | 19 | var serviceProvider = services.BuildServiceProvider(); 20 | 21 | var mediator = serviceProvider.GetRequiredService(); 22 | 23 | var id = Guid.NewGuid(); 24 | var ping = new StreamPing(id); 25 | 26 | await foreach (var pong in mediator.CreateStream(ping)) 27 | { 28 | Console.WriteLine("-----------------------------------"); 29 | Console.WriteLine("ID: " + id); 30 | Console.WriteLine(ping); 31 | Console.WriteLine(pong); 32 | } 33 | 34 | return 0; 35 | 36 | // 37 | // Here are the types used 38 | // 39 | 40 | public sealed record StreamPing(Guid Id) : IStreamRequest; 41 | 42 | public sealed record Pong(Guid Id); 43 | 44 | public sealed class PingHandler : IStreamRequestHandler 45 | { 46 | public async IAsyncEnumerable Handle( 47 | StreamPing request, 48 | [EnumeratorCancellation] CancellationToken cancellationToken 49 | ) 50 | { 51 | for (int i = 0; i < 5; i++) 52 | { 53 | await Task.Delay(1000, cancellationToken); 54 | yield return new Pong(request.Id); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /samples/basic/Streaming/README.md: -------------------------------------------------------------------------------- 1 | ## Streaming 2 | 3 | Sample of streaming an `IAsyncEnumerable` of messages. 4 | 5 | ### Build and run 6 | 7 | ```console 8 | $ dotnet run 9 | ----------------------------------- 10 | ID: 93365b37-c597-4ebf-9f30-63327536efc8 11 | StreamPing { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 12 | Pong { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 13 | ----------------------------------- 14 | ID: 93365b37-c597-4ebf-9f30-63327536efc8 15 | StreamPing { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 16 | Pong { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 17 | ----------------------------------- 18 | ID: 93365b37-c597-4ebf-9f30-63327536efc8 19 | StreamPing { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 20 | Pong { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 21 | ----------------------------------- 22 | ID: 93365b37-c597-4ebf-9f30-63327536efc8 23 | StreamPing { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 24 | Pong { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 25 | ----------------------------------- 26 | ID: 93365b37-c597-4ebf-9f30-63327536efc8 27 | StreamPing { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 28 | Pong { Id = 93365b37-c597-4ebf-9f30-63327536efc8 } 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /samples/basic/Streaming/Streaming.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | disable 6 | 7 | 8 | true 9 | 10 | Generated 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /samples/use-cases/Autofac_DI/Autofac_DI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | true 9 | 10 | Generated 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /samples/use-cases/Autofac_DI/Program.cs: -------------------------------------------------------------------------------- 1 | using Autofac.Extensions.DependencyInjection; 2 | using Mediator; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | var builder = WebApplication.CreateBuilder(args); 6 | 7 | builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); 8 | 9 | builder.Services.AddMediator( 10 | (MediatorOptions options) => 11 | { 12 | options.Namespace = "Foo.Generated"; 13 | options.Assemblies = [typeof(Ping)]; 14 | options.ServiceLifetime = ServiceLifetime.Scoped; 15 | } 16 | ); 17 | 18 | var app = builder.Build(); 19 | 20 | app.MapGet( 21 | "/", 22 | async ([FromServices] Foo.Generated.Mediator mediator) => 23 | { 24 | var id = Guid.NewGuid(); 25 | var request = new Ping(id); 26 | var response = await mediator.Send(request); 27 | return Results.Ok(response); 28 | } 29 | ); 30 | 31 | app.Run(); 32 | 33 | public sealed record Ping(Guid Id) : IRequest; 34 | 35 | public sealed record Pong(Guid Id); 36 | 37 | public sealed class PingHandler : IRequestHandler 38 | { 39 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 40 | { 41 | return new ValueTask(new Pong(request.Id)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /samples/use-cases/Autofac_DI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Autofac_DI": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "launchBrowser": false, 7 | "applicationUrl": "http://localhost:5000", 8 | "environmentVariables": { 9 | "ASPNETCORE_ENVIRONMENT": "Development" 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /samples/use-cases/Autofac_DI/README.md: -------------------------------------------------------------------------------- 1 | ## Autofac DI 2 | 3 | Sample of using Mediator with the Autofac DI container with ASP.NET Core minimal APIs. 4 | 5 | ### Build and run 6 | 7 | ```console 8 | $ dotnet run 9 | Building... 10 | info: Microsoft.Hosting.Lifetime[14] 11 | Now listening on: http://localhost:5000 12 | info: Microsoft.Hosting.Lifetime[0] 13 | Application started. Press Ctrl+C to shut down. 14 | info: Microsoft.Hosting.Lifetime[0] 15 | Hosting environment: Development 16 | info: Microsoft.Hosting.Lifetime[0] 17 | Content root path: /home/martin/code/private/Mediator/samples/use-cases/Autofac_DI 18 | ``` 19 | 20 | Open [http://localhost:5000](http://localhost:5000) to test the API. 21 | -------------------------------------------------------------------------------- /samples/use-cases/Autofac_DI/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/use-cases/Autofac_DI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /samples/use-cases/MassTransitIntegration/MassTransitIntegration.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | true 9 | 10 | Generated 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | README.md 4 | LICENSE 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Analysis/InvocationSyntaxReceiver.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal sealed class SyntaxReceiver : ISyntaxReceiver 4 | { 5 | public List AddMediatorCalls { get; } = new List(); 6 | 7 | public void OnVisitSyntaxNode(SyntaxNode context) 8 | { 9 | if (ShouldVisit(context, out var invocationSyntax)) 10 | AddMediatorCalls.Add(invocationSyntax!); 11 | } 12 | 13 | public static bool ShouldVisit(SyntaxNode context, out InvocationExpressionSyntax? invocation) 14 | { 15 | invocation = null; 16 | if ( 17 | context 18 | is not InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax identifier } invocationSyntax 19 | ) 20 | return false; 21 | if (identifier.Name.Identifier.ValueText != "AddMediator") 22 | return false; 23 | 24 | invocation = invocationSyntax; 25 | return true; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Analysis/MessageHandler.cs: -------------------------------------------------------------------------------- 1 | using Mediator.SourceGenerator.Extensions; 2 | 3 | namespace Mediator.SourceGenerator; 4 | 5 | internal abstract class MessageHandler : SymbolMetadata> 6 | { 7 | protected MessageHandler(INamedTypeSymbol symbol, CompilationAnalyzer analyzer) 8 | : base(symbol, analyzer) { } 9 | 10 | public string FullName => Symbol.GetTypeSymbolFullName(); 11 | } 12 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Analysis/NotificationMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal sealed class NotificationMessage : SymbolMetadata 4 | { 5 | public NotificationMessage(INamedTypeSymbol symbol, CompilationAnalyzer analyzer) 6 | : base(symbol, analyzer) { } 7 | 8 | public NotificationMessageModel ToModel() 9 | { 10 | return new NotificationMessageModel(Symbol, Analyzer); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Analysis/PipelineBehaviorType.cs: -------------------------------------------------------------------------------- 1 | using Mediator.SourceGenerator.Extensions; 2 | 3 | namespace Mediator.SourceGenerator; 4 | 5 | internal sealed class PipelineBehaviorType : SymbolMetadata 6 | { 7 | public readonly INamedTypeSymbol InterfaceSymbol; 8 | private readonly List _messages; 9 | 10 | public IReadOnlyList Messages => _messages; 11 | 12 | public PipelineBehaviorType(INamedTypeSymbol symbol, INamedTypeSymbol interfaceSymbol, CompilationAnalyzer analyzer) 13 | : base(symbol, analyzer) 14 | { 15 | InterfaceSymbol = symbol.AllInterfaces.Single(i => 16 | i.IsGenericType && i.OriginalDefinition.Equals(interfaceSymbol, SymbolEqualityComparer.Default) 17 | ); 18 | _messages = new List(); 19 | } 20 | 21 | public void TryAddMessage(RequestMessage message) 22 | { 23 | if (Symbol.IsGenericType && Symbol.TypeParameters.Length == 2) 24 | { 25 | var messageSymbol = message.Symbol; 26 | var responseSymbol = message.ResponseSymbol; 27 | 28 | var compilation = Analyzer.Compilation; 29 | if (Symbol.SatisfiesConstraints([messageSymbol, responseSymbol], compilation)) 30 | { 31 | _messages.Add(message); 32 | } 33 | } 34 | else 35 | { 36 | var requestType = InterfaceSymbol.TypeArguments[0]; 37 | if (message.Symbol.Equals(requestType, SymbolEqualityComparer.Default)) 38 | { 39 | _messages.Add(message); 40 | } 41 | } 42 | } 43 | 44 | public PipelineBehaviorModel ToModel() 45 | { 46 | return new PipelineBehaviorModel(this, Analyzer); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Analysis/RequestMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal sealed class RequestMessage : SymbolMetadata 4 | { 5 | public RequestMessage( 6 | INamedTypeSymbol symbol, 7 | ITypeSymbol responseSymbol, 8 | string messageType, 9 | CompilationAnalyzer analyzer 10 | ) 11 | : base(symbol, analyzer) 12 | { 13 | ResponseSymbol = responseSymbol; 14 | WrapperType = analyzer.RequestMessageHandlerWrappers.Single(w => w.MessageType == messageType); 15 | MessageType = messageType; 16 | } 17 | 18 | public RequestMessageHandler? Handler { get; private set; } 19 | 20 | public ITypeSymbol ResponseSymbol { get; } 21 | 22 | public RequestMessageHandlerWrapperModel WrapperType { get; } 23 | 24 | public string MessageType { get; } 25 | 26 | public void SetHandler(RequestMessageHandler handler) => Handler = handler; 27 | } 28 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Analysis/RequestMessageHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal sealed class RequestMessageHandler : MessageHandler 4 | { 5 | private readonly string _messageType; 6 | private readonly RequestMessageHandlerWrapperModel _wrapperType; 7 | 8 | public RequestMessageHandler(INamedTypeSymbol symbol, string messageType, CompilationAnalyzer analyzer) 9 | : base(symbol, analyzer) 10 | { 11 | _messageType = messageType; 12 | _wrapperType = analyzer.RequestMessageHandlerWrappers.Single(w => w.MessageType == messageType); 13 | } 14 | 15 | public RequestMessageHandlerModel ToModel() 16 | { 17 | return new RequestMessageHandlerModel(Symbol, _messageType, Analyzer, _wrapperType); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Analysis/SymbolMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal abstract class SymbolMetadata : IEquatable 4 | where T : SymbolMetadata 5 | { 6 | private static readonly SymbolEqualityComparer _comparer = SymbolEqualityComparer.Default; 7 | public readonly INamedTypeSymbol Symbol; 8 | protected readonly CompilationAnalyzer Analyzer; 9 | 10 | protected SymbolMetadata(INamedTypeSymbol symbol, CompilationAnalyzer analyzer) 11 | { 12 | Symbol = symbol; 13 | Analyzer = analyzer; 14 | } 15 | 16 | public override bool Equals(object? obj) => Equals(obj as T); 17 | 18 | public bool Equals(T? other) => other != null && _comparer.Equals(Symbol, other.Symbol); 19 | 20 | public override int GetHashCode() => _comparer.GetHashCode(Symbol); 21 | 22 | public override string ToString() => Symbol.Name; 23 | 24 | public string AccessibilityModifier => 25 | Symbol.DeclaredAccessibility switch 26 | { 27 | Accessibility.Internal => "internal", 28 | _ => "public", 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal static class Constants 4 | { 5 | public static readonly string MediatorLib = "Mediator"; 6 | } 7 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/EmbeddedResource.cs: -------------------------------------------------------------------------------- 1 | // Copyright @kzu 2 | // License MIT 3 | // copied from https://github.com/devlooped/ThisAssembly/blob/main/src/EmbeddedResource.cs 4 | 5 | using System.Reflection; 6 | 7 | internal static class EmbeddedResource 8 | { 9 | public static string GetContent(string relativePath) 10 | { 11 | var baseName = Assembly.GetExecutingAssembly().GetName().Name; 12 | var resourceName = relativePath 13 | .TrimStart('.') 14 | .Replace(Path.DirectorySeparatorChar, '.') 15 | .Replace(Path.AltDirectorySeparatorChar, '.'); 16 | 17 | var manifestResourceName = Assembly 18 | .GetExecutingAssembly() 19 | .GetManifestResourceNames() 20 | .FirstOrDefault(x => x!.EndsWith(resourceName, StringComparison.InvariantCulture)); 21 | 22 | if (string.IsNullOrEmpty(manifestResourceName)) 23 | throw new InvalidOperationException( 24 | $"Did not find required resource ending in '{resourceName}' in assembly '{baseName}'." 25 | ); 26 | 27 | using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(manifestResourceName); 28 | 29 | if (stream == null) 30 | throw new InvalidOperationException( 31 | $"Did not find required resource '{manifestResourceName}' in assembly '{baseName}'." 32 | ); 33 | 34 | using var reader = new StreamReader(stream); 35 | return reader.ReadToEnd(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/MediatorGenerationContext.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal readonly struct MediatorGenerationContext 4 | { 5 | public readonly IReadOnlyDictionary> HandlerMap; 6 | public readonly IEnumerable HandlerTypes; 7 | public readonly string MediatorNamespace; 8 | 9 | public MediatorGenerationContext( 10 | IReadOnlyDictionary> handlerMap, 11 | IEnumerable handlerTypes, 12 | string mediatorNamespace 13 | ) 14 | { 15 | HandlerMap = handlerMap; 16 | HandlerTypes = handlerTypes; 17 | MediatorNamespace = mediatorNamespace; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Models/NotificationMessageModel.cs: -------------------------------------------------------------------------------- 1 | using Mediator.SourceGenerator.Extensions; 2 | 3 | namespace Mediator.SourceGenerator; 4 | 5 | internal sealed record NotificationMessageModel : SymbolMetadataModel 6 | { 7 | public NotificationMessageModel(INamedTypeSymbol symbol, CompilationAnalyzer analyzer) 8 | : base(symbol) 9 | { 10 | var identifierFullName = symbol 11 | .GetTypeSymbolFullName(withGlobalPrefix: false, includeTypeParameters: false) 12 | .Replace("global::", "") 13 | .Replace('.', '_'); 14 | var handlerWrapperNamespace = $"global::{analyzer.MediatorNamespace}.Internals"; 15 | HandlerWrapperTypeNameWithGenericTypeArguments = 16 | $"{handlerWrapperNamespace}.NotificationHandlerWrapper<{FullName}>"; 17 | HandlerWrapperPropertyName = $"Wrapper_For_{identifierFullName}"; 18 | } 19 | 20 | public string HandlerWrapperTypeNameWithGenericTypeArguments { get; } 21 | 22 | public string HandlerWrapperPropertyName { get; } 23 | } 24 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Models/NotificationPublisherTypeModel.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal readonly record struct NotificationPublisherTypeModel(string FullName, string Name); 4 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Models/PipelineBehaviorModel.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using Mediator.SourceGenerator.Extensions; 3 | 4 | namespace Mediator.SourceGenerator; 5 | 6 | internal sealed record PipelineBehaviorModel : SymbolMetadataModel 7 | { 8 | public PipelineBehaviorModel(PipelineBehaviorType type, CompilationAnalyzer analyzer) 9 | : base(type.Symbol) 10 | { 11 | ServiceRegistrations = ImmutableEquatableArray.Empty; 12 | if (type.Messages.Count > 0) 13 | { 14 | var interfaceSymbol = type.InterfaceSymbol.GetTypeSymbolFullName(includeTypeParameters: false); 15 | var concreteSymbol = type.Symbol.GetTypeSymbolFullName(includeTypeParameters: false); 16 | var builder = new List(type.Messages.Count); 17 | foreach (var message in type.Messages) 18 | { 19 | var requestType = message.Symbol.GetTypeSymbolFullName(); 20 | var responseType = message.ResponseSymbol.GetTypeSymbolFullName(); 21 | var registration = 22 | $"services.Add(new SD(typeof({interfaceSymbol}<{requestType}, {responseType}>), typeof({concreteSymbol}{(type.Symbol.IsGenericType ? $"<{requestType}, {responseType}>" : "")}), {analyzer.ServiceLifetime}));"; 23 | builder.Add(registration); 24 | } 25 | 26 | ServiceRegistrations = builder.ToImmutableEquatableArray(); 27 | } 28 | } 29 | 30 | public ImmutableEquatableArray ServiceRegistrations { get; } 31 | } 32 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Models/RequestMessageHandlerModel.cs: -------------------------------------------------------------------------------- 1 | using Mediator.SourceGenerator.Extensions; 2 | 3 | namespace Mediator.SourceGenerator; 4 | 5 | internal sealed record RequestMessageHandlerModel : SymbolMetadataModel 6 | { 7 | public string MessageType { get; } 8 | public RequestMessageHandlerWrapperModel WrapperType { get; } 9 | 10 | public RequestMessageHandlerModel( 11 | INamedTypeSymbol symbol, 12 | string messageType, 13 | CompilationAnalyzer analyzer, 14 | RequestMessageHandlerWrapperModel wrapperType 15 | ) 16 | : base(symbol) 17 | { 18 | MessageType = messageType; 19 | WrapperType = wrapperType; 20 | 21 | var typeOfExpression = $"typeof({symbol.GetTypeSymbolFullName()})"; 22 | ServiceRegistration = 23 | $"services.TryAdd(new SD({typeOfExpression}, {typeOfExpression}, {analyzer.ServiceLifetime}));"; 24 | } 25 | 26 | public string ServiceRegistration { get; } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Models/SymbolMetadataModel.cs: -------------------------------------------------------------------------------- 1 | using Mediator.SourceGenerator.Extensions; 2 | 3 | namespace Mediator.SourceGenerator; 4 | 5 | internal abstract record SymbolMetadataModel 6 | { 7 | protected SymbolMetadataModel(INamedTypeSymbol symbol) 8 | { 9 | Name = symbol.Name; 10 | FullName = symbol.GetTypeSymbolFullName(); 11 | Kind = symbol.TypeKind; 12 | IsReadOnly = symbol.IsReadOnly; 13 | AccessibilityModifier = symbol.DeclaredAccessibility is Accessibility.Internal ? "internal" : "public"; 14 | } 15 | 16 | public string Name { get; } 17 | 18 | public string FullName { get; } 19 | 20 | public TypeKind Kind { get; } 21 | 22 | public bool IsStruct => Kind == TypeKind.Struct; 23 | 24 | public bool IsClass => !IsStruct; 25 | 26 | public bool IsReadOnly { get; } 27 | 28 | public string AccessibilityModifier { get; } 29 | 30 | public override string ToString() => Name; 31 | } 32 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/Versioning.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | public static class Versioning 4 | { 5 | public static string GetVersion() 6 | { 7 | var generatorAssembly = typeof(Versioning).Assembly; 8 | var generatorVersion = generatorAssembly.GetName().Version.ToString(); 9 | return generatorVersion; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/resources/AssemblyReference.sbn-cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the Mediator source generator. 3 | // 4 | 5 | namespace Mediator 6 | { 7 | /// 8 | /// Represents an assembly reference. 9 | /// This is used to specify the types or assemblies to scan for Mediator handlers. 10 | /// 11 | [global::System.CodeDom.Compiler.GeneratedCode("Mediator.SourceGenerator", "{{ GeneratorVersion }}")] 12 | {{ TypeAccessibility }} sealed class AssemblyReference 13 | { 14 | /// 15 | /// The assembly reference. 16 | /// 17 | public global::System.Reflection.Assembly Assembly { get; } 18 | 19 | private AssemblyReference(global::System.Reflection.Assembly assembly) 20 | { 21 | Assembly = assembly; 22 | } 23 | 24 | /// 25 | /// Creates a new instance of from the specified type. 26 | /// 27 | /// The type 28 | /// A new instance of 29 | public static implicit operator AssemblyReference(global::System.Type type) => new AssemblyReference(type.Assembly); 30 | 31 | /// 32 | /// Creates a new instance of from the specified assembly. 33 | /// 34 | /// The assembly 35 | /// A new instance of 36 | public static implicit operator AssemblyReference(global::System.Reflection.Assembly assembly) => new AssemblyReference(assembly); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Implementation/resources/MediatorOptionsAttribute.sbn-cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the Mediator source generator. 3 | // 4 | 5 | namespace Mediator 6 | { 7 | /// 8 | /// Provide options for the Mediator source generator. 9 | /// 10 | [global::System.AttributeUsage(global::System.AttributeTargets.Assembly, AllowMultiple = false)] 11 | [global::System.CodeDom.Compiler.GeneratedCode("Mediator.SourceGenerator", "{{ GeneratorVersion }}")] 12 | {{ TypeAccessibility }} sealed class MediatorOptionsAttribute : global::System.Attribute 13 | { 14 | /// 15 | /// The namespace in which the Mediator implementation is generated. 16 | /// By default, the namespace is "Mediator". 17 | /// 18 | public string Namespace { get; set; } = "Mediator"; 19 | 20 | /// 21 | /// The type to use when publishing notifications. 22 | /// By default, the type is . 23 | /// 24 | public global::System.Type NotificationPublisherType { get; set; } = typeof(global::Mediator.ForeachAwaitPublisher); 25 | 26 | /// 27 | /// The default lifetime of the services registered in the DI container by the Mediator source generator. 28 | /// By default, the lifetime is . 29 | /// 30 | public global::Microsoft.Extensions.DependencyInjection.ServiceLifetime ServiceLifetime { get; set; } = 31 | global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/MediatorGeneratorStepName.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.SourceGenerator; 2 | 3 | internal static class MediatorGeneratorStepName 4 | { 5 | public const string ReportDiagnostics = "ReportDiagnostics"; 6 | public const string BuildMediator = "BuildMediator"; 7 | } 8 | -------------------------------------------------------------------------------- /src/Mediator.SourceGenerator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Mediator.SourceGenerator": { 4 | "commandName": "DebugRoslynComponent", 5 | "targetProject": "..\\..\\samples\\Console\\Console.csproj" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Mediator/Handlers/ICommandHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface ICommandHandler 4 | where TCommand : ICommand 5 | { 6 | ValueTask Handle(TCommand command, CancellationToken cancellationToken); 7 | } 8 | 9 | public interface ICommandHandler : ICommandHandler 10 | where TCommand : ICommand { } 11 | -------------------------------------------------------------------------------- /src/Mediator/Handlers/INotificationHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface INotificationHandler 4 | where TNotification : INotification 5 | { 6 | ValueTask Handle(TNotification notification, CancellationToken cancellationToken); 7 | } 8 | -------------------------------------------------------------------------------- /src/Mediator/Handlers/IQueryHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IQueryHandler 4 | where TQuery : IQuery 5 | { 6 | ValueTask Handle(TQuery query, CancellationToken cancellationToken); 7 | } 8 | -------------------------------------------------------------------------------- /src/Mediator/Handlers/IRequestHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IRequestHandler 4 | where TRequest : IRequest 5 | { 6 | ValueTask Handle(TRequest request, CancellationToken cancellationToken); 7 | } 8 | 9 | public interface IRequestHandler : IRequestHandler 10 | where TRequest : IRequest { } 11 | -------------------------------------------------------------------------------- /src/Mediator/Handlers/IStreamCommandHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IStreamCommandHandler 4 | where TCommand : IStreamCommand 5 | { 6 | IAsyncEnumerable Handle(TCommand command, CancellationToken cancellationToken); 7 | } 8 | -------------------------------------------------------------------------------- /src/Mediator/Handlers/IStreamQueryHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IStreamQueryHandler 4 | where TQuery : IStreamQuery 5 | { 6 | IAsyncEnumerable Handle(TQuery query, CancellationToken cancellationToken); 7 | } 8 | -------------------------------------------------------------------------------- /src/Mediator/Handlers/IStreamRequestHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IStreamRequestHandler 4 | where TRequest : IStreamRequest 5 | { 6 | IAsyncEnumerable Handle(TRequest request, CancellationToken cancellationToken); 7 | } 8 | -------------------------------------------------------------------------------- /src/Mediator/IMediator.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | /// 4 | /// Mediator instance for sending requests, commands, queries and their streaming counterparts (). 5 | /// Also for publishing notifications. 6 | /// Use the concrete Mediator implementation for the highest performance (monomorphized method overloads per T available). 7 | /// Can use the and for requests/commands/queries and notifications respectively. 8 | /// 9 | public interface IMediator : ISender, IPublisher { } 10 | -------------------------------------------------------------------------------- /src/Mediator/IPublisher.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | /// 4 | /// Mediator instance for publishing notifications 5 | /// 6 | public interface IPublisher 7 | { 8 | /// 9 | /// Publish notification. 10 | /// Throws if message is null. 11 | /// Throws if notification does not implement . 12 | /// Throws if handlers throw exceptions. 13 | /// 14 | /// Incoming notification 15 | /// Cancellation token 16 | /// Awaitable task 17 | ValueTask Publish(TNotification notification, CancellationToken cancellationToken = default) 18 | where TNotification : INotification; 19 | 20 | /// 21 | /// Publish notification. 22 | /// Throws if message is null. 23 | /// Throws if notification does not implement . 24 | /// Throws if handlers throw exception(s). 25 | /// Drops messages 26 | /// 27 | /// Incoming notification 28 | /// Cancellation token 29 | /// Awaitable task 30 | ValueTask Publish(object notification, CancellationToken cancellationToken = default); 31 | } 32 | -------------------------------------------------------------------------------- /src/Mediator/InvalidMessageException.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | /// 4 | /// Exception thrown when Mediator receives messages that don't derive from the correct interfaces 5 | /// from Mediator.Abstractions. 6 | /// 7 | public class InvalidMessageException : Exception 8 | { 9 | public object? MediatorMessage { get; } 10 | 11 | public InvalidMessageException(object? message) 12 | : base("Tried to send/publish invalid message type to Mediator: " + message?.GetType().FullName ?? "Unknown") 13 | { 14 | MediatorMessage = message; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Mediator/Mediator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0;net8.0 4 | Mediator.Abstractions 5 | true 6 | 7 | Abstractions for the Mediator.SourceGenerator package. 8 | 9 | true 10 | 11 | 12 | true 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | all 34 | runtime; build; native; contentfiles; analyzers 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IBaseCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IBaseCommand : IMessage { } 4 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IBaseQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IBaseQuery : IMessage { } 4 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IBaseRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IBaseRequest : IMessage { } 4 | -------------------------------------------------------------------------------- /src/Mediator/Messages/ICommand.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface ICommand : ICommand { } 4 | 5 | public interface ICommand : IBaseCommand { } 6 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IMessage { } 4 | -------------------------------------------------------------------------------- /src/Mediator/Messages/INotification.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface INotification : IMessage { } 4 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IQuery : IBaseQuery { } 4 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IRequest : IRequest { } 4 | 5 | public interface IRequest : IBaseRequest { } 6 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IStreamCommand.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IBaseStreamCommand : IStreamMessage { } 4 | 5 | public interface IStreamCommand : IBaseStreamCommand { } 6 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IStreamMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IStreamMessage { } 4 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IStreamQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IBaseStreamQuery : IStreamMessage { } 4 | 5 | public interface IStreamQuery : IBaseStreamQuery { } 6 | -------------------------------------------------------------------------------- /src/Mediator/Messages/IStreamRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IBaseStreamRequest : IStreamMessage { } 4 | 5 | public interface IStreamRequest : IBaseStreamRequest { } 6 | -------------------------------------------------------------------------------- /src/Mediator/Messages/Unit.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator 2 | { 3 | public readonly struct Unit : IEquatable, IComparable, IComparable 4 | { 5 | private static readonly Unit _value = new(); 6 | 7 | public static ref readonly Unit Value => ref _value; 8 | 9 | public static ValueTask ValueTask => new ValueTask(_value); 10 | 11 | public int CompareTo(Unit other) => 0; 12 | 13 | int IComparable.CompareTo(object? obj) => 0; 14 | 15 | public override int GetHashCode() => 0; 16 | 17 | public bool Equals(Unit other) => true; 18 | 19 | public override bool Equals(object? obj) => obj is Unit; 20 | 21 | public static bool operator ==(Unit _, Unit __) => true; 22 | 23 | public static bool operator !=(Unit _, Unit __) => false; 24 | 25 | public override string ToString() => "()"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Mediator/MissingMessageHandlerException.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | /// 4 | /// Exception that is thrown when Mediator receives messages 5 | /// that have no registered handlers. 6 | /// 7 | public class MissingMessageHandlerException : Exception 8 | { 9 | public object? MediatorMessage { get; } 10 | 11 | public MissingMessageHandlerException(object? message) 12 | : base("No handler registered for message type: " + message?.GetType().FullName ?? "Unknown") 13 | { 14 | MediatorMessage = message; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Mediator/Module.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [module: SkipLocalsInit] 4 | -------------------------------------------------------------------------------- /src/Mediator/Pipeline/IPipelineBehavior.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IPipelineBehavior 4 | where TMessage : notnull, IMessage 5 | { 6 | ValueTask Handle( 7 | TMessage message, 8 | MessageHandlerDelegate next, 9 | CancellationToken cancellationToken 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/Mediator/Pipeline/IStreamPipelineBehavior.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public interface IStreamPipelineBehavior 4 | where TMessage : IStreamMessage 5 | { 6 | IAsyncEnumerable Handle( 7 | TMessage message, 8 | StreamHandlerDelegate next, 9 | CancellationToken cancellationToken 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/Mediator/Pipeline/MessageHandlerDelegate.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public delegate ValueTask MessageHandlerDelegate( 4 | TMessage message, 5 | CancellationToken cancellationToken 6 | ) 7 | where TMessage : notnull, IMessage; 8 | -------------------------------------------------------------------------------- /src/Mediator/Pipeline/MessagePostProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public abstract class MessagePostProcessor : IPipelineBehavior 4 | where TMessage : notnull, IMessage 5 | { 6 | public async ValueTask Handle( 7 | TMessage message, 8 | MessageHandlerDelegate next, 9 | CancellationToken cancellationToken 10 | ) 11 | { 12 | var response = await next(message, cancellationToken); 13 | await Handle(message, response, cancellationToken); 14 | return response; 15 | } 16 | 17 | protected abstract ValueTask Handle(TMessage message, TResponse response, CancellationToken cancellationToken); 18 | } 19 | -------------------------------------------------------------------------------- /src/Mediator/Pipeline/MessagePreProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public abstract class MessagePreProcessor : IPipelineBehavior 4 | where TMessage : notnull, IMessage 5 | { 6 | public ValueTask Handle( 7 | TMessage message, 8 | MessageHandlerDelegate next, 9 | CancellationToken cancellationToken 10 | ) 11 | { 12 | var task = Handle(message, cancellationToken); 13 | if (task.IsCompletedSuccessfully) 14 | return next(message, cancellationToken); 15 | 16 | return HandleInternal(task, message, next, cancellationToken); 17 | 18 | static async ValueTask HandleInternal( 19 | ValueTask task, 20 | TMessage message, 21 | MessageHandlerDelegate next, 22 | CancellationToken cancellationToken 23 | ) 24 | { 25 | await task; 26 | return await next(message, cancellationToken); 27 | } 28 | } 29 | 30 | protected abstract ValueTask Handle(TMessage message, CancellationToken cancellationToken); 31 | } 32 | -------------------------------------------------------------------------------- /src/Mediator/Pipeline/StreamHandlerDelegate.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator; 2 | 3 | public delegate IAsyncEnumerable StreamHandlerDelegate( 4 | TMessage message, 5 | CancellationToken cancellationToken 6 | ) 7 | where TMessage : notnull, IStreamMessage; 8 | -------------------------------------------------------------------------------- /src/Mediator/ThrowHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace Mediator; 4 | 5 | internal static class ThrowHelper 6 | { 7 | [DoesNotReturn] 8 | internal static void ThrowInvalidOperationException(string message) => throw new InvalidOperationException(message); 9 | 10 | [DoesNotReturn] 11 | internal static void ThrowAggregateException(List exceptions) => 12 | throw new AggregateException(exceptions); 13 | } 14 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | disable 5 | 6 | 7 | 8 | $(DefineConstants);$(ExtraDefineConstants) 9 | 10 | 11 | -------------------------------------------------------------------------------- /test/Mediator.MemAllocationTests/Allocations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator.MemAllocationTests; 4 | 5 | public static class Allocations 6 | { 7 | public static long GetCurrentThreadAllocatedBytes() 8 | { 9 | // Run before and after message sending to test allocations. 10 | // Stolen from https://github.com/dotnet/BenchmarkDotNet/blob/4bd433d85fff4fb6ba8c4f8df3e685ad669e2519/src/BenchmarkDotNet/Engines/GcStats.cs#L132 11 | // There is a reason GC.Collect is run first 12 | GC.Collect(); 13 | return GC.GetAllocatedBytesForCurrentThread(); 14 | } 15 | 16 | public static long GetCurrentAllocatedBytes() 17 | { 18 | // Run before and after message sending to test allocations. 19 | // Stolen from https://github.com/dotnet/BenchmarkDotNet/blob/4bd433d85fff4fb6ba8c4f8df3e685ad669e2519/src/BenchmarkDotNet/Engines/GcStats.cs#L132 20 | // There is a reason GC.Collect is run first 21 | GC.Collect(); 22 | return GC.GetTotalAllocatedBytes(true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/Mediator.MemAllocationTests/Mediator.MemAllocationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | false 5 | true 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | all 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/Mediator.MemAllocationTests/NonParallelCollectionDefinitionClass.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.Tests; 2 | 3 | [CollectionDefinition("Non-Parallel", DisableParallelization = true)] 4 | public class NonParallelCollectionDefinitionClass { } 5 | -------------------------------------------------------------------------------- /test/Mediator.MemAllocationTests/NotificationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace Mediator.MemAllocationTests; 5 | 6 | [Collection("Non-Parallel")] 7 | public class NotificationTests 8 | { 9 | [Fact] 10 | public void Test_Notification_Handler() 11 | { 12 | var services = new ServiceCollection(); 13 | 14 | services.AddMediator(); 15 | 16 | using var sp = services.BuildServiceProvider( 17 | new ServiceProviderOptions() { ValidateOnBuild = true, ValidateScopes = true } 18 | ); 19 | var mediator = sp.GetRequiredService(); 20 | 21 | var id = Guid.NewGuid(); 22 | var notification = new SomeNotificationMemAllocTracking(id); 23 | 24 | // Ensure everything is cached 25 | mediator.Publish(notification); // Everything returns sync 26 | 27 | var beforeBytes = Allocations.GetCurrentThreadAllocatedBytes(); 28 | mediator.Publish(notification); // Everything returns sync 29 | var afterBytes = Allocations.GetCurrentThreadAllocatedBytes(); 30 | 31 | Assert.Equal(0, afterBytes - beforeBytes); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/Mediator.MemAllocationTests/SomeNotificationMemAllocTracking.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Mediator.MemAllocationTests; 6 | 7 | public sealed record SomeNotificationMemAllocTracking(Guid Id) : INotification; 8 | 9 | public sealed class SomeNotificationMemAllocTrackingHandler : INotificationHandler 10 | { 11 | public ValueTask Handle(SomeNotificationMemAllocTracking notification, CancellationToken cancellationToken) => 12 | default; 13 | } 14 | -------------------------------------------------------------------------------- /test/Mediator.MemAllocationTests/SomeRequestMemAllocTracking.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Mediator.MemAllocationTests; 6 | 7 | public sealed record SomeRequestMemAllocTracking(Guid Id) : IRequest; 8 | 9 | public sealed class SomeRequestMemAllocTrackingHandler : IRequestHandler 10 | { 11 | public ValueTask Handle(SomeRequestMemAllocTracking request, CancellationToken cancellationToken) => default; 12 | } 13 | -------------------------------------------------------------------------------- /test/Mediator.SmokeTestConsole/Mediator.SmokeTestConsole.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | true 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/AssertExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.CodeAnalysis; 3 | using Microsoft.CodeAnalysis.CSharp; 4 | using VerifyTests; 5 | using VerifyXunit; 6 | 7 | namespace Mediator.SourceGenerator.Tests; 8 | 9 | public static class AssertExtensions 10 | { 11 | public static SettingsTask AssertAndVerify( 12 | this Compilation inputCompilation, 13 | params Action[] assertionDelegates 14 | ) 15 | { 16 | return AssertAndVerify( 17 | inputCompilation, 18 | r => r.HintName.Contains("MediatorOptions") || r.HintName.Contains("AssemblyReference"), 19 | assertionDelegates 20 | ); 21 | } 22 | 23 | public static SettingsTask AssertAndVerify( 24 | this Compilation inputCompilation, 25 | Func? ignoreGeneratedResult, 26 | params Action[] assertionDelegates 27 | ) 28 | { 29 | var generator = new IncrementalMediatorGenerator(); 30 | 31 | GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); 32 | 33 | driver = driver.RunGeneratorsAndUpdateCompilation( 34 | inputCompilation, 35 | out var outputCompilation, 36 | out var diagnostics 37 | ); 38 | 39 | var runResult = driver.GetRunResult(); 40 | 41 | var result = new GeneratorResult(generator, diagnostics, runResult, outputCompilation); 42 | 43 | foreach (var assertions in assertionDelegates) 44 | assertions(result); 45 | 46 | var verifier = Verifier.Verify(driver); 47 | if (ignoreGeneratedResult is not null) 48 | verifier = verifier.IgnoreGeneratedResult(r => ignoreGeneratedResult(r)); 49 | return verifier; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/Assertions.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.CodeAnalysis; 3 | 4 | namespace Mediator.SourceGenerator.Tests; 5 | 6 | public static class Assertions 7 | { 8 | public static void NoMediatorImplGenerated(GeneratorResult result) 9 | { 10 | Assert.Single(result.RunResult.GeneratedTrees); 11 | } 12 | 13 | public static void AssertCommon(GeneratorResult result) 14 | { 15 | var analyzer = result.Generator.CompilationAnalyzer; 16 | 17 | Assert.NotNull(analyzer); 18 | Assert.True( 19 | analyzer!.ServiceLifetimeIsSingleton 20 | || analyzer.ServiceLifetimeIsScoped 21 | || analyzer.ServiceLifetimeIsTransient 22 | ); 23 | } 24 | 25 | public static void CompilesWithoutDiagnostics(GeneratorResult result) 26 | { 27 | AssertCommon(result); 28 | 29 | Assert.True(result.Diagnostics.IsEmpty); 30 | Assert.True(result.RunResult.Diagnostics.IsEmpty); 31 | 32 | var outputCompilationDiagnostics = result.OutputCompilation.GetDiagnostics(); 33 | Assert.Empty(outputCompilationDiagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)); 34 | } 35 | 36 | public static void CompilesWithoutErrorDiagnostics(GeneratorResult result) 37 | { 38 | AssertCommon(result); 39 | 40 | Assert.Empty(result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)); 41 | Assert.Empty(result.RunResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)); 42 | 43 | var outputCompilationDiagnostics = result.OutputCompilation.GetDiagnostics(); 44 | Assert.Empty(outputCompilationDiagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/GeneratorResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Immutable; 2 | using Microsoft.CodeAnalysis; 3 | 4 | namespace Mediator.SourceGenerator.Tests; 5 | 6 | public sealed record GeneratorResult( 7 | IncrementalMediatorGenerator Generator, 8 | ImmutableArray Diagnostics, 9 | GeneratorDriverRunResult RunResult, 10 | Compilation OutputCompilation 11 | ); 12 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/Incremental/IncrementalGeneratorRunReason.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CodeAnalysis; 2 | 3 | namespace Mediator.SourceGenerator.Tests.Incremental; 4 | 5 | internal record IncrementalGeneratorRunReasons( 6 | IncrementalStepRunReason BuildMediatorStep, 7 | IncrementalStepRunReason ReportDiagnosticsStep 8 | ) 9 | { 10 | public static readonly IncrementalGeneratorRunReasons New = new( 11 | IncrementalStepRunReason.New, 12 | IncrementalStepRunReason.New 13 | ); 14 | 15 | public static readonly IncrementalGeneratorRunReasons Cached = new( 16 | // compilation step should always be modified as each time a new compilation is passed 17 | IncrementalStepRunReason.Cached, 18 | IncrementalStepRunReason.Cached 19 | ); 20 | 21 | public static readonly IncrementalGeneratorRunReasons Modified = Cached with 22 | { 23 | ReportDiagnosticsStep = IncrementalStepRunReason.Modified, 24 | BuildMediatorStep = IncrementalStepRunReason.Modified, 25 | }; 26 | 27 | public static readonly IncrementalGeneratorRunReasons ModifiedSource = Cached with 28 | { 29 | ReportDiagnosticsStep = IncrementalStepRunReason.Unchanged, 30 | BuildMediatorStep = IncrementalStepRunReason.Modified, 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/ModuleInitializer.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using DiffEngine; 3 | using Microsoft.Build.Locator; 4 | using VerifyTests; 5 | using VerifyXunit; 6 | 7 | namespace Mediator.SourceGenerator.Tests; 8 | 9 | public static class ModuleInitializer 10 | { 11 | private static VisualStudioInstance? _instance; 12 | 13 | [ModuleInitializer] 14 | public static void Init() 15 | { 16 | _instance ??= MSBuildLocator.RegisterDefaults(); 17 | 18 | DiffRunner.Disabled = true; 19 | 20 | Verifier.DerivePathInfo( 21 | (file, _, type, method) => new(Path.Join(Path.GetDirectoryName(file), "_snapshots"), type.Name, method.Name) 22 | ); 23 | 24 | VerifySourceGenerators.Initialize(); 25 | VerifyDiffPlex.Initialize(VerifyTests.DiffPlex.OutputType.Compact); 26 | VerifierSettings.AutoVerify(includeBuildServer: false); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ConfigurationTests.Test_Assemblies_Duplicate_Configuration.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | ]; 6 | options.Assemblies = 7 | ^^^^^^^^^^^^^^^^^^ 8 | [ 9 | */ 10 | : (20,12)-(20,30), 11 | Message: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. Assemblies can only be configured once, 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0007, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. {0}, 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ConfigurationTests.Test_Assemblies_Duplicate_Reference.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | typeof(Program), 6 | typeof(Program), 7 | ^^^^^^^ 8 | ]; 9 | */ 10 | : (19,23)-(19,30), 11 | Message: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. The assembly 'Library' is already configured, 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0007, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. {0}, 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ConfigurationTests.Test_Assemblies_Invalid_Reference.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | [ 6 | typeof(object), 7 | ^^^^^^ 8 | ]; 9 | */ 10 | : (18,23)-(18,29), 11 | Message: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. Configured assembly does not reference 'Mediator.Abstractions', so it cannot have messages and handlers, 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0007, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. {0}, 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ConfigurationTests.Test_Assemblies_Mediator_Abstractions_Reference.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | [ 6 | typeof(Unit), 7 | ^^^^ 8 | ]; 9 | */ 10 | : (18,23)-(18,27), 11 | Message: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. Tried to configure 'Mediator.Abstractions' as an assembly, 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0007, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. {0}, 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ConfigurationTests.Test_GenerateTypesAsInternal_Non_Literal.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | { 6 | options.GenerateTypesAsInternal = _generateTypesAsInternal; 7 | ^^^^^^^^^^^^^^^^^^^^^^^^ 8 | }); 9 | */ 10 | : (18,46)-(18,70), 11 | Message: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. Expected literal expression for 'GenerateTypesAsInternal', 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0007, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. {0}, 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ConfigurationTests.Test_GenerateTypesAsInternal_value=False#AssemblyReference.g.verified.cs: -------------------------------------------------------------------------------- 1 | //HintName: AssemblyReference.g.cs 2 | // 3 | // Generated by the Mediator source generator. 4 | // 5 | 6 | namespace Mediator 7 | { 8 | /// 9 | /// Represents an assembly reference. 10 | /// This is used to specify the types or assemblies to scan for Mediator handlers. 11 | /// 12 | [global::System.CodeDom.Compiler.GeneratedCode("Mediator.SourceGenerator", "3.0.0.0")] 13 | public sealed class AssemblyReference 14 | { 15 | /// 16 | /// The assembly reference. 17 | /// 18 | public global::System.Reflection.Assembly Assembly { get; } 19 | 20 | private AssemblyReference(global::System.Reflection.Assembly assembly) 21 | { 22 | Assembly = assembly; 23 | } 24 | 25 | /// 26 | /// Creates a new instance of from the specified type. 27 | /// 28 | /// The type 29 | /// A new instance of 30 | public static implicit operator AssemblyReference(global::System.Type type) => new AssemblyReference(type.Assembly); 31 | 32 | /// 33 | /// Creates a new instance of from the specified assembly. 34 | /// 35 | /// The assembly 36 | /// A new instance of 37 | public static implicit operator AssemblyReference(global::System.Reflection.Assembly assembly) => new AssemblyReference(assembly); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ConfigurationTests.Test_GenerateTypesAsInternal_value=True#AssemblyReference.g.verified.cs: -------------------------------------------------------------------------------- 1 | //HintName: AssemblyReference.g.cs 2 | // 3 | // Generated by the Mediator source generator. 4 | // 5 | 6 | namespace Mediator 7 | { 8 | /// 9 | /// Represents an assembly reference. 10 | /// This is used to specify the types or assemblies to scan for Mediator handlers. 11 | /// 12 | [global::System.CodeDom.Compiler.GeneratedCode("Mediator.SourceGenerator", "3.0.0.0")] 13 | internal sealed class AssemblyReference 14 | { 15 | /// 16 | /// The assembly reference. 17 | /// 18 | public global::System.Reflection.Assembly Assembly { get; } 19 | 20 | private AssemblyReference(global::System.Reflection.Assembly assembly) 21 | { 22 | Assembly = assembly; 23 | } 24 | 25 | /// 26 | /// Creates a new instance of from the specified type. 27 | /// 28 | /// The type 29 | /// A new instance of 30 | public static implicit operator AssemblyReference(global::System.Type type) => new AssemblyReference(type.Assembly); 31 | 32 | /// 33 | /// Creates a new instance of from the specified assembly. 34 | /// 35 | /// The assembly 36 | /// A new instance of 37 | public static implicit operator AssemblyReference(global::System.Reflection.Assembly assembly) => new AssemblyReference(assembly); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ConfigurationTests.Test_TaskWhenAllPublisher_For_Notifications_Program#MediatorOptions.g.verified.cs: -------------------------------------------------------------------------------- 1 | //HintName: MediatorOptions.g.cs 2 | // 3 | // Generated by the Mediator source generator. 4 | // 5 | 6 | namespace Mediator 7 | { 8 | /// 9 | /// Provide options for the Mediator source generator. 10 | /// 11 | [global::System.CodeDom.Compiler.GeneratedCode("Mediator.SourceGenerator", "3.0.0.0")] 12 | public sealed class MediatorOptions 13 | { 14 | /// 15 | /// The namespace in which the Mediator implementation is generated. 16 | /// By default, the namespace is "Mediator". 17 | /// 18 | public string Namespace { get; set; } = "Mediator"; 19 | 20 | /// 21 | /// The type to use when publishing notifications. 22 | /// By default, the type is . 23 | /// 24 | public global::System.Type NotificationPublisherType { get; set; } = typeof(global::Mediator.ForeachAwaitPublisher); 25 | 26 | /// 27 | /// The default lifetime of the services registered in the DI container by the Mediator source generator. 28 | /// By default, the lifetime is . 29 | /// 30 | public global::Microsoft.Extensions.DependencyInjection.ServiceLifetime ServiceLifetime { get; set; } = 31 | global::Microsoft.Extensions.DependencyInjection.ServiceLifetime.Singleton; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Abstract_Handler_Program.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | 6 | public sealed record Ping(Guid Id) : IRequest; 7 | ^^^^ 8 | 9 | */ 10 | : (30,29)-(30,33), 11 | Message: MediatorGenerator found message without any registered handler: Some.Nested.Types.Program.Ping, 12 | Severity: Warning, 13 | WarningLevel: 1, 14 | Descriptor: { 15 | Id: MSG0005, 16 | Title: MediatorGenerator message warning, 17 | MessageFormat: MediatorGenerator found message without any registered handler: {0}, 18 | Category: MediatorGenerator, 19 | DefaultSeverity: Warning, 20 | IsEnabledByDefault: true 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Cast_Lifetime_Config.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | { 6 | options.ServiceLifetime = (ServiceLifetime)(int)ServiceLifetime.Transient; 7 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8 | }); 9 | */ 10 | : (16,42)-(16,89), 11 | Message: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. Could not resolve lifetime configuration, 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0007, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. {0}, 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Configuratoin_Conflict.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | 6 | [assembly: MediatorOptions(Namespace = "SimpleConsole.Mediator", ServiceLifetime = ServiceLifetime.Transient)] 7 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8 | 9 | */ 10 | : (6,11)-(6,109), 11 | Message: MediatorGenerator found conflicting configuration - both MediatorOptions and MediatorOptionsAttribute configuration are being used., 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0006, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator found conflicting configuration - both MediatorOptions and MediatorOptionsAttribute configuration are being used., 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Duplicate_Handlers.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | 6 | public sealed class DuplicatePingHandler : IRequestHandler 7 | ^^^^^^^^^^^^^^^^^^^^ 8 | { 9 | */ 10 | : (35,20)-(35,40), 11 | Message: MediatorGenerator found multiple handlers of message type DuplicatePingHandler, 12 | Severity: Warning, 13 | WarningLevel: 1, 14 | Descriptor: { 15 | Id: MSG0001, 16 | Title: MediatorGenerator multiple handlers, 17 | MessageFormat: MediatorGenerator found multiple handlers of message type {0}, 18 | Category: MediatorGenerator, 19 | DefaultSeverity: Warning, 20 | IsEnabledByDefault: true 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Invalid_Handler_Type.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | 6 | public readonly struct PingHandler : IRequestHandler 7 | ^^^^^^^^^^^ 8 | { 9 | */ 10 | : (27,23)-(27,34), 11 | Message: MediatorGenerator found invalid handler type PingHandler, 12 | Severity: Warning, 13 | WarningLevel: 1, 14 | Descriptor: { 15 | Id: MSG0002, 16 | Title: MediatorGenerator invalid handler, 17 | MessageFormat: MediatorGenerator found invalid handler type {0}, 18 | Category: MediatorGenerator, 19 | DefaultSeverity: Warning, 20 | IsEnabledByDefault: true 21 | } 22 | }, 23 | { 24 | Location: /* 25 | 26 | public sealed record Ping(Guid Id) : IRequest; 27 | ^^^^ 28 | 29 | */ 30 | : (23,21)-(23,25), 31 | Message: MediatorGenerator found message without any registered handler: Ping, 32 | Severity: Warning, 33 | WarningLevel: 1, 34 | Descriptor: { 35 | Id: MSG0005, 36 | Title: MediatorGenerator message warning, 37 | MessageFormat: MediatorGenerator found message without any registered handler: {0}, 38 | Category: MediatorGenerator, 39 | DefaultSeverity: Warning, 40 | IsEnabledByDefault: true 41 | } 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Invalid_Variable_In_Config.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | { 6 | options.Namespace = MediatorNamespace; 7 | ^^^^^^^^^^^^^^^^^ 8 | options.ServiceLifetime = Lifetime; 9 | */ 10 | : (19,36)-(19,53), 11 | Message: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. Could not resolve namespace configuration, 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0007, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. {0}, 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Multiple_Errors.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | 6 | public sealed class DuplicatePingHandler : IRequestHandler 7 | ^^^^^^^^^^^^^^^^^^^^ 8 | { 9 | */ 10 | : (35,20)-(35,40), 11 | Message: MediatorGenerator found multiple handlers of message type DuplicatePingHandler, 12 | Severity: Warning, 13 | WarningLevel: 1, 14 | Descriptor: { 15 | Id: MSG0001, 16 | Title: MediatorGenerator multiple handlers, 17 | MessageFormat: MediatorGenerator found multiple handlers of message type {0}, 18 | Category: MediatorGenerator, 19 | DefaultSeverity: Warning, 20 | IsEnabledByDefault: true 21 | } 22 | }, 23 | { 24 | Location: /* 25 | 26 | public readonly struct StructPingHandler : IRequestHandler 27 | ^^^^^^^^^^^^^^^^^ 28 | { 29 | */ 30 | : (43,23)-(43,40), 31 | Message: MediatorGenerator found invalid handler type StructPingHandler, 32 | Severity: Warning, 33 | WarningLevel: 1, 34 | Descriptor: { 35 | Id: MSG0002, 36 | Title: MediatorGenerator invalid handler, 37 | MessageFormat: MediatorGenerator found invalid handler type {0}, 38 | Category: MediatorGenerator, 39 | DefaultSeverity: Warning, 40 | IsEnabledByDefault: true 41 | } 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Notification_Without_Any_Handlers.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | 6 | public sealed record Ping(Guid Id) : INotification; 7 | ^^^^ 8 | } 9 | */ 10 | : (30,29)-(30,33), 11 | Message: MediatorGenerator found message without any registered handler: Some.Nested.Types.Program.Ping, 12 | Severity: Warning, 13 | WarningLevel: 1, 14 | Descriptor: { 15 | Id: MSG0005, 16 | Title: MediatorGenerator message warning, 17 | MessageFormat: MediatorGenerator found message without any registered handler: {0}, 18 | Category: MediatorGenerator, 19 | DefaultSeverity: Warning, 20 | IsEnabledByDefault: true 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Request_Without_Handler_In_Referenced_Library.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Message: MediatorGenerator found message without any registered handler: TestCode.Library1.Request1, 5 | Severity: Warning, 6 | WarningLevel: 1, 7 | Descriptor: { 8 | Id: MSG0005, 9 | Title: MediatorGenerator message warning, 10 | MessageFormat: MediatorGenerator found message without any registered handler: {0}, 11 | Category: MediatorGenerator, 12 | DefaultSeverity: Warning, 13 | IsEnabledByDefault: true 14 | } 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Request_Without_Handler_Warning.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | 6 | public sealed record Ping(Guid Id) : IRequest; 7 | ^^^^ 8 | 9 | */ 10 | : (30,29)-(30,33), 11 | Message: MediatorGenerator found message without any registered handler: Some.Nested.Types.Program.Ping, 12 | Severity: Warning, 13 | WarningLevel: 1, 14 | Descriptor: { 15 | Id: MSG0005, 16 | Title: MediatorGenerator message warning, 17 | MessageFormat: MediatorGenerator found message without any registered handler: {0}, 18 | Category: MediatorGenerator, 19 | DefaultSeverity: Warning, 20 | IsEnabledByDefault: true 21 | } 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/_snapshots/ReportingTests.Test_Unassigned_Lifetime_Variable_In_Config.verified.txt: -------------------------------------------------------------------------------- 1 | { 2 | Diagnostics: [ 3 | { 4 | Location: /* 5 | { 6 | private static ServiceLifetime Lifetime { get; } 7 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8 | 9 | */ 10 | : (10,8)-(10,56), 11 | Message: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. Failed to resolve lifetime configuration, 12 | Severity: Error, 13 | Descriptor: { 14 | Id: MSG0007, 15 | Title: MediatorGenerator configuration error, 16 | MessageFormat: MediatorGenerator could not parse MediatorOptions-based configuration. Only compile-time constant values can be used in MediatorOptions configuration. {0}, 17 | Category: MediatorGenerator, 18 | DefaultSeverity: Error, 19 | IsEnabledByDefault: true 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/AbstractHandlerProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddMediator(); 16 | 17 | var serviceProvider = services.BuildServiceProvider(); 18 | 19 | var mediator = serviceProvider.GetRequiredService(); 20 | 21 | var id = Guid.NewGuid(); 22 | var request = new Ping(id); 23 | 24 | _ = await mediator.Send(request); 25 | } 26 | 27 | // 28 | // Types 29 | // 30 | 31 | public sealed record Ping(Guid Id) : IRequest; 32 | 33 | public sealed record Pong(Guid Id); 34 | 35 | public abstract class PingHandler : IRequestHandler 36 | { 37 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 38 | { 39 | return new ValueTask(new Pong(request.Id)); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/ByteArrayResponseProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Mediator; 7 | using Microsoft.Extensions.DependencyInjection; 8 | 9 | namespace Some.Nested.Types 10 | { 11 | public static class Program 12 | { 13 | public static async Task Main() 14 | { 15 | var services = new ServiceCollection(); 16 | 17 | services.AddMediator(); 18 | 19 | var serviceProvider = services.BuildServiceProvider(); 20 | 21 | var mediator = serviceProvider.GetRequiredService(); 22 | 23 | _ = await mediator.Send(new Ping(Guid.NewGuid())); 24 | } 25 | 26 | // 27 | // Types 28 | // 29 | 30 | public sealed record Ping(Guid Id) : IRequest; 31 | 32 | public sealed class PingHandler : IRequestHandler 33 | { 34 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 35 | { 36 | var bytes = request.Id.ToByteArray(); 37 | return new ValueTask(bytes); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/ConfigurationConflictProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | [assembly: MediatorOptions(Namespace = "SimpleConsole.Mediator", ServiceLifetime = ServiceLifetime.Transient)] 8 | 9 | namespace Some.Nested.Types 10 | { 11 | public static class Program 12 | { 13 | public static async Task Main() 14 | { 15 | var services = new ServiceCollection(); 16 | 17 | services.AddMediator(options => 18 | { 19 | options.Namespace = "SimpleConsole.Mediator"; 20 | options.ServiceLifetime = ServiceLifetime.Transient; 21 | }); 22 | 23 | var serviceProvider = services.BuildServiceProvider(); 24 | 25 | var mediator = serviceProvider.GetRequiredService(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/ConstVariablesConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | private const string MediatorNamespace = "SimpleConsole.Mediator"; 12 | private const ServiceLifetime Lifetime = ServiceLifetime.Transient; 13 | 14 | public static async Task Main() 15 | { 16 | var services = new ServiceCollection(); 17 | 18 | services.AddMediator(options => 19 | { 20 | options.Namespace = MediatorNamespace; 21 | options.ServiceLifetime = Lifetime; 22 | }); 23 | 24 | var serviceProvider = services.BuildServiceProvider(); 25 | 26 | var mediator = serviceProvider.GetRequiredService(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/DeepNamespaceProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Very.Very.Very.Very.Deep.Namespace.ThatIUseToTestTheSourceGenSoThatItCanHandleLotsOfDifferentInput 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddMediator(); 16 | 17 | var serviceProvider = services.BuildServiceProvider(); 18 | 19 | var mediator = serviceProvider.GetRequiredService(); 20 | 21 | var id = Guid.NewGuid(); 22 | var request = new Ping(id); 23 | 24 | _ = await mediator.Send(request); 25 | } 26 | } 27 | 28 | // 29 | // Types 30 | // 31 | 32 | public sealed record Ping(Guid Id) : IRequest; 33 | 34 | public sealed record Pong(Guid Id); 35 | 36 | public sealed class PingHandler : IRequestHandler 37 | { 38 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 39 | { 40 | return new ValueTask(new Pong(request.Id)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/DuplicateHandlersProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | var services = new ServiceCollection(); 8 | 9 | services.AddMediator(); 10 | 11 | var serviceProvider = services.BuildServiceProvider(); 12 | 13 | var mediator = serviceProvider.GetRequiredService(); 14 | 15 | var id = Guid.NewGuid(); 16 | var request = new Ping(id); 17 | 18 | _ = await mediator.Send(request); 19 | 20 | // 21 | // Types 22 | // 23 | 24 | public sealed record Ping(Guid Id) : IRequest; 25 | 26 | public sealed record Pong(Guid Id); 27 | 28 | public sealed class PingHandler : IRequestHandler 29 | { 30 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 31 | { 32 | return new ValueTask(new Pong(request.Id)); 33 | } 34 | } 35 | 36 | public sealed class DuplicatePingHandler : IRequestHandler 37 | { 38 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 39 | { 40 | return new ValueTask(new Pong(request.Id)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/IntCastLifetime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddMediator(options => 16 | { 17 | options.ServiceLifetime = (ServiceLifetime)(int)ServiceLifetime.Transient; 18 | }); 19 | 20 | var serviceProvider = services.BuildServiceProvider(); 21 | 22 | var mediator = serviceProvider.GetRequiredService(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/InvalidVariablesConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | private static readonly string MediatorNamespace = Environment.GetEnvironmentVariable("NS"); 12 | private static readonly ServiceLifetime Lifetime = ServiceLifetime.Transient; 13 | 14 | public static async Task Main() 15 | { 16 | var services = new ServiceCollection(); 17 | 18 | services.AddMediator(options => 19 | { 20 | options.Namespace = MediatorNamespace; 21 | options.ServiceLifetime = Lifetime; 22 | }); 23 | 24 | var serviceProvider = services.BuildServiceProvider(); 25 | 26 | var mediator = serviceProvider.GetRequiredService(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/LocalLiteralVariableConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | var ns = "SomeNamespace"; 16 | var lifetime = ServiceLifetime.Scoped; 17 | 18 | services.AddMediator(options => 19 | { 20 | options.Namespace = ns; 21 | options.ServiceLifetime = lifetime; 22 | }); 23 | 24 | var serviceProvider = services.BuildServiceProvider(); 25 | 26 | var mediator = serviceProvider.GetRequiredService(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/LocalVariablesReferencingConstsConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | private const string NS = "SomeNamespace"; 12 | private const ServiceLifetime Lifetime = ServiceLifetime.Scoped; 13 | 14 | private static readonly string NSStaticReadonly = NS; 15 | private static readonly ServiceLifetime LifetimeStaticReadOnly = Lifetime; 16 | 17 | private static string NSStaticProp { get; } = NSStaticReadonly; 18 | private static ServiceLifetime LifetimeStaticProp { get; } = LifetimeStaticReadOnly; 19 | 20 | public static async Task Main() 21 | { 22 | var services = new ServiceCollection(); 23 | 24 | var ns = NSStaticProp; 25 | var lifetime = LifetimeStaticProp; 26 | 27 | services.AddMediator(options => 28 | { 29 | options.Namespace = ns; 30 | options.ServiceLifetime = lifetime; 31 | }); 32 | 33 | var serviceProvider = services.BuildServiceProvider(); 34 | 35 | var mediator = serviceProvider.GetRequiredService(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/MultipleAddMediatorCalls.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Mediator; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Some.Nested.Types 6 | { 7 | public static class Program 8 | { 9 | public static void Main() 10 | { 11 | var services = new ServiceCollection(); 12 | 13 | services.AddMediator((Mediator.MediatorOptions opts) => opts.ServiceLifetime = ServiceLifetime.Scoped); 14 | 15 | var serviceProvider = services.BuildServiceProvider(); 16 | 17 | var mediator = serviceProvider.GetRequiredService(); 18 | } 19 | } 20 | } 21 | 22 | namespace Microsoft.Extensions.DependencyInjection 23 | { 24 | public static class SomeDIExtension 25 | { 26 | public static IServiceCollection AddMediator(this IServiceCollection services, Action options) 27 | { 28 | return services; 29 | } 30 | } 31 | 32 | public sealed class MediatorOptions 33 | { 34 | public string Namespace { get; set; } = "Mediator"; 35 | 36 | public ServiceLifetime ServiceLifetime { get; set; } = ServiceLifetime.Singleton; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/MultipleErrorsProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | var services = new ServiceCollection(); 8 | 9 | services.AddMediator(); 10 | 11 | var serviceProvider = services.BuildServiceProvider(); 12 | 13 | var mediator = serviceProvider.GetRequiredService(); 14 | 15 | var id = Guid.NewGuid(); 16 | var request = new Ping(id); 17 | 18 | _ = await mediator.Send(request); 19 | 20 | // 21 | // Types 22 | // 23 | 24 | public sealed record Ping(Guid Id) : IRequest; 25 | 26 | public sealed record Pong(Guid Id); 27 | 28 | public sealed class PingHandler : IRequestHandler 29 | { 30 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 31 | { 32 | return new ValueTask(new Pong(request.Id)); 33 | } 34 | } 35 | 36 | public sealed class DuplicatePingHandler : IRequestHandler 37 | { 38 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 39 | { 40 | return new ValueTask(new Pong(request.Id)); 41 | } 42 | } 43 | 44 | public readonly struct StructPingHandler : IRequestHandler 45 | { 46 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 47 | { 48 | return new ValueTask(new Pong(request.Id)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/NoMessagesProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddMediator(); 16 | 17 | var serviceProvider = services.BuildServiceProvider(); 18 | 19 | var mediator = serviceProvider.GetRequiredService(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/NotificationWithoutHandlerProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddMediator(); 16 | 17 | var serviceProvider = services.BuildServiceProvider(); 18 | 19 | var mediator = serviceProvider.GetRequiredService(); 20 | 21 | var id = Guid.NewGuid(); 22 | var request = new Ping(id); 23 | 24 | await mediator.Publish(request); 25 | } 26 | 27 | // 28 | // Types 29 | // 30 | 31 | public sealed record Ping(Guid Id) : INotification; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/NullNamespaceVariable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddMediator(options => 16 | { 17 | options.Namespace = null; 18 | }); 19 | 20 | var serviceProvider = services.BuildServiceProvider(); 21 | 22 | var mediator = serviceProvider.GetRequiredService(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/RequestWithoutHandlerProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddMediator(); 16 | 17 | var serviceProvider = services.BuildServiceProvider(); 18 | 19 | var mediator = serviceProvider.GetRequiredService(); 20 | 21 | var id = Guid.NewGuid(); 22 | var request = new Ping(id); 23 | 24 | _ = await mediator.Send(request); 25 | } 26 | 27 | // 28 | // Types 29 | // 30 | 31 | public sealed record Ping(Guid Id) : IRequest; 32 | 33 | public sealed record Pong(Guid Id); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/StaticNestedHandlerProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | public static async Task Main() 12 | { 13 | var services = new ServiceCollection(); 14 | 15 | services.AddMediator(); 16 | 17 | var serviceProvider = services.BuildServiceProvider(); 18 | 19 | var mediator = serviceProvider.GetRequiredService(); 20 | 21 | var id = Guid.NewGuid(); 22 | var request = new Ping(id); 23 | 24 | _ = await mediator.Send(request); 25 | } 26 | 27 | // 28 | // Types 29 | // 30 | 31 | public sealed record Ping(Guid Id) : IRequest; 32 | 33 | public sealed record Pong(Guid Id); 34 | 35 | public sealed class PingHandler : IRequestHandler 36 | { 37 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 38 | { 39 | return new ValueTask(new Pong(request.Id)); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/StructHandlerProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | var services = new ServiceCollection(); 8 | 9 | services.AddMediator(); 10 | 11 | var serviceProvider = services.BuildServiceProvider(); 12 | 13 | var mediator = serviceProvider.GetRequiredService(); 14 | 15 | var id = Guid.NewGuid(); 16 | var request = new Ping(id); 17 | 18 | _ = await mediator.Send(request); 19 | 20 | // 21 | // Types 22 | // 23 | 24 | public sealed record Ping(Guid Id) : IRequest; 25 | 26 | public sealed record Pong(Guid Id); 27 | 28 | public readonly struct PingHandler : IRequestHandler 29 | { 30 | public ValueTask Handle(Ping request, CancellationToken cancellationToken) 31 | { 32 | return new ValueTask(new Pong(request.Id)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/UnassignedLifetimeVariableConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | private static ServiceLifetime Lifetime { get; } 12 | 13 | public static async Task Main() 14 | { 15 | var services = new ServiceCollection(); 16 | 17 | services.AddMediator(options => 18 | { 19 | options.ServiceLifetime = Lifetime; 20 | }); 21 | 22 | var serviceProvider = services.BuildServiceProvider(); 23 | 24 | var mediator = serviceProvider.GetRequiredService(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/UnassignedNamespaceVariableConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | private static string MediatorNamespace { get; } 12 | 13 | public static async Task Main() 14 | { 15 | var services = new ServiceCollection(); 16 | 17 | services.AddMediator(options => 18 | { 19 | options.Namespace = MediatorNamespace; 20 | }); 21 | 22 | var serviceProvider = services.BuildServiceProvider(); 23 | 24 | var mediator = serviceProvider.GetRequiredService(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/Mediator.SourceGenerator.Tests/resources/UnassignedVariablesConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Some.Nested.Types 8 | { 9 | public static class Program 10 | { 11 | private static string MediatorNamespace { get; } 12 | private static ServiceLifetime Lifetime { get; } 13 | 14 | public static async Task Main() 15 | { 16 | var services = new ServiceCollection(); 17 | 18 | services.AddMediator(options => 19 | { 20 | options.Namespace = MediatorNamespace; 21 | options.ServiceLifetime = Lifetime; 22 | }); 23 | 24 | var serviceProvider = services.BuildServiceProvider(); 25 | 26 | var mediator = serviceProvider.GetRequiredService(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Mediator.Tests/Assertions.cs: -------------------------------------------------------------------------------- 1 | global using static Mediator.Tests.Assertions; 2 | using System; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using Microsoft.Extensions.DependencyInjection; 6 | 7 | namespace Mediator.Tests; 8 | 9 | #pragma warning disable CS0162 // Unreachable code detected 10 | 11 | internal static class Assertions 12 | { 13 | public static void AssertInstanceIdCount(int expected, ConcurrentDictionary instanceIds, Guid id) 14 | { 15 | if (Mediator.ServiceLifetime == ServiceLifetime.Transient) 16 | return; 17 | Assert.Equal(expected, instanceIds.GetValueOrDefault(id, 0)); 18 | } 19 | 20 | public static void AssertInstanceIdCount( 21 | int expected, 22 | ConcurrentDictionary instanceIds, 23 | Guid id, 24 | long timestampBefore, 25 | long timestampAfter 26 | ) 27 | { 28 | if (Mediator.ServiceLifetime == ServiceLifetime.Transient) 29 | return; 30 | var data = instanceIds.GetValueOrDefault(id, default); 31 | Assert.Equal(expected, data.Count); 32 | Assert.True(data.Timestamp > timestampBefore && data.Timestamp < timestampAfter); 33 | } 34 | } 35 | 36 | #pragma warning restore CS0162 // Unreachable code detected 37 | -------------------------------------------------------------------------------- /test/Mediator.Tests/ConfigurationOutput.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Xunit.Abstractions; 3 | 4 | namespace Mediator.Tests; 5 | 6 | public class ConfigurationOutput 7 | { 8 | private readonly ITestOutputHelper output; 9 | 10 | public ConfigurationOutput(ITestOutputHelper output) 11 | { 12 | this.output = output; 13 | } 14 | 15 | [Fact] 16 | public void Test() 17 | { 18 | var (sp, _) = Fixture.GetMediator(); 19 | 20 | var publisher = sp.GetRequiredService(); 21 | 22 | output.WriteLine(""); 23 | output.WriteLine(""); 24 | output.WriteLine(""); 25 | output.WriteLine("------------------------------------------------------------------------------"); 26 | output.WriteLine("Mediator configuration:"); 27 | output.WriteLine($" ServiceLifetime: {Mediator.ServiceLifetime}"); 28 | output.WriteLine( 29 | $" NotificationPublisherType: {publisher.GetType().FullName} ({Mediator.NotificationPublisherName})" 30 | ); 31 | output.WriteLine($" Message count: {Mediator.TotalMessages}"); 32 | output.WriteLine("------------------------------------------------------------------------------"); 33 | output.WriteLine(""); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/Mediator.Tests/HandlerInjectsMediatorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Mediator.Tests; 7 | 8 | public class HandlerInjectsMediatorTests 9 | { 10 | public sealed record Request(Guid Id) : IRequest; 11 | 12 | public sealed class Handler : IRequestHandler 13 | { 14 | public static readonly ConcurrentBag Ids = new(); 15 | 16 | public Handler(IMediator mediator) 17 | { 18 | Assert.NotNull(mediator); 19 | } 20 | 21 | public ValueTask Handle(Request request, CancellationToken cancellationToken) 22 | { 23 | Ids.Add(request.Id); 24 | return default; 25 | } 26 | } 27 | 28 | [Fact] 29 | public async Task Test_Invoke_Handler_Which_Injects_IMediator() 30 | { 31 | var (_, mediator) = Fixture.GetMediator(); 32 | Assert.NotNull(mediator); 33 | 34 | var id = Guid.NewGuid(); 35 | await mediator.Send(new Request(id)); 36 | Assert.Contains(id, Handler.Ids); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/Mediator.Tests/NonParallelCollectionDefinitionClass.cs: -------------------------------------------------------------------------------- 1 | namespace Mediator.Tests; 2 | 3 | [CollectionDefinition("Non-Parallel", DisableParallelization = true)] 4 | public class NonParallelCollectionDefinitionClass { } 5 | -------------------------------------------------------------------------------- /test/Mediator.Tests/NullableResponseTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Mediator.Tests.TestTypes; 5 | 6 | namespace Mediator.Tests; 7 | 8 | public sealed record RequestWithNullableResponse(Guid Id) : IRequest; 9 | 10 | public sealed class RequestWithNullableResponseHandler : IRequestHandler 11 | { 12 | public ValueTask Handle(RequestWithNullableResponse request, CancellationToken cancellationToken) => 13 | new ValueTask(default(SomeResponse?)); 14 | } 15 | 16 | public class NullableResponseTests 17 | { 18 | [Fact] 19 | public async Task Test_Request_With_Nullable_Response() 20 | { 21 | var (_, mediator) = Fixture.GetMediator(); 22 | var concrete = (Mediator)mediator; 23 | 24 | var id = Guid.NewGuid(); 25 | 26 | var response = await mediator.Send(new RequestWithNullableResponse(id)); 27 | Assert.Null(response); 28 | 29 | var response2 = await concrete.Send(new RequestWithNullableResponse(id)); 30 | Assert.Null(response2); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/Mediator.Tests/Pipeline/CommandSpecificPipeline.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Mediator.Tests.Pipeline; 5 | 6 | public sealed class CommandSpecificPipeline : IPipelineBehavior 7 | where TCommand : IBaseCommand 8 | { 9 | public static int CallCount { get; private set; } 10 | 11 | public ValueTask Handle( 12 | TCommand message, 13 | MessageHandlerDelegate next, 14 | CancellationToken cancellationToken 15 | ) 16 | { 17 | CallCount++; 18 | return next(message, cancellationToken); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/Mediator.Tests/Pipeline/IPipelineTestData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator.Tests.Pipeline; 4 | 5 | public interface IPipelineTestData 6 | { 7 | Guid Id { get; } 8 | 9 | public long LastMsgTimestamp { get; } 10 | } 11 | -------------------------------------------------------------------------------- /test/Mediator.Tests/Pipeline/SomePipeline.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Mediator.Tests.TestTypes; 6 | 7 | namespace Mediator.Tests.Pipeline; 8 | 9 | public sealed class SomePipeline(bool earlyReturn = false) 10 | : IPipelineBehavior, 11 | IPipelineTestData 12 | { 13 | public Guid Id { get; private set; } 14 | public long LastMsgTimestamp { get; private set; } 15 | private readonly bool _earlyReturn = earlyReturn; 16 | 17 | public ValueTask Handle( 18 | SomeRequest message, 19 | MessageHandlerDelegate next, 20 | CancellationToken cancellationToken 21 | ) 22 | { 23 | LastMsgTimestamp = Stopwatch.GetTimestamp(); 24 | 25 | if (_earlyReturn) 26 | { 27 | var response = new SomeResponse(message.Id) { ReturnedEarly = true }; 28 | 29 | return new ValueTask(response); 30 | } 31 | 32 | if (message is null || message.Id == default) 33 | throw new ArgumentException("Invalid input"); 34 | 35 | Id = message.Id; 36 | 37 | return next(message, cancellationToken); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/Mediator.Tests/Pipeline/SomeStreamingPipeline.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.CompilerServices; 3 | using System.Threading; 4 | using Mediator.Tests.TestTypes; 5 | 6 | namespace Mediator.Tests.Pipeline; 7 | 8 | public sealed class SomeStreamingPipeline : IStreamPipelineBehavior 9 | { 10 | public async IAsyncEnumerable Handle( 11 | SomeStreamingQuery message, 12 | StreamHandlerDelegate next, 13 | [EnumeratorCancellation] CancellationToken cancellationToken 14 | ) 15 | { 16 | await foreach (var response in next(message, cancellationToken)) 17 | { 18 | response.SomeStreamingData = 1; 19 | yield return response; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/Mediator.Tests/Pipeline/StreamingPipelineTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Mediator.Tests.TestTypes; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace Mediator.Tests.Pipeline; 7 | 8 | public sealed class StreamingPipelineTests 9 | { 10 | [Fact] 11 | public async Task Test_Pipeline() 12 | { 13 | var (sp, mediator) = Fixture.GetMediator(services => 14 | { 15 | services.AddSingleton, SomeStreamingPipeline>(); 16 | }); 17 | 18 | var id = Guid.NewGuid(); 19 | 20 | int counter = 0; 21 | await foreach (var response in mediator.CreateStream(new SomeStreamingQuery(id))) 22 | { 23 | Assert.Equal(id, response.Id); 24 | Assert.Equal(1, response.SomeStreamingData); 25 | counter++; 26 | } 27 | } 28 | 29 | [Fact] 30 | public async Task Test_Generic_Pipeline() 31 | { 32 | var (sp, mediator) = Fixture.GetMediator(services => 33 | { 34 | services.AddSingleton(); 35 | services.AddSingleton(typeof(IStreamPipelineBehavior<,>), typeof(GenericStreamPipeline<,>)); 36 | }); 37 | 38 | var query = new SomeStreamingQuery(Guid.NewGuid()); 39 | 40 | var pipelineState = sp.GetRequiredService(); 41 | 42 | Assert.Equal(default, pipelineState.Id); 43 | Assert.Equal(default, pipelineState.Message); 44 | 45 | await foreach (var response in mediator.CreateStream(query)) 46 | { 47 | Assert.Equal(query.Id, pipelineState.Id); 48 | Assert.Equal(query, pipelineState.Message); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/Mediator.Tests/Scoped/Init.cs: -------------------------------------------------------------------------------- 1 | #if Mediator_Lifetime_Scoped 2 | 3 | using Xunit.Abstractions; 4 | using Xunit.Sdk; 5 | 6 | [assembly: TestFramework("Mediator.Tests.Framework", "Mediator.Tests")] 7 | 8 | namespace Mediator.Tests; 9 | 10 | public class Framework : XunitTestFramework 11 | { 12 | public Framework(IMessageSink messageSink) 13 | : base(messageSink) 14 | { 15 | Fixture.CreateServiceScope = true; 16 | } 17 | 18 | public new void Dispose() 19 | { 20 | // Place tear down code here 21 | base.Dispose(); 22 | } 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /test/Mediator.Tests/SingletonLifetimeTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace Mediator.Tests; 4 | 5 | public class SingletonLifetimeTests 6 | { 7 | [Fact(Skip = Mediator.ServiceLifetime != ServiceLifetime.Singleton ? "Only tested for Singleton lifetime" : null)] 8 | public void Test_Generated_Code_Lifetime() 9 | { 10 | var (_, mediator) = Fixture.GetMediator(); 11 | Assert.NotNull(mediator); 12 | 13 | Assert.Equal(ServiceLifetime.Singleton, Mediator.ServiceLifetime); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/Mediator.Tests/SmokeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Mediator.Tests.TestTypes; 4 | 5 | namespace Mediator.Tests; 6 | 7 | public partial class SmokeTests 8 | { 9 | [Theory] 10 | [InlineData(1L << 2)] 11 | [InlineData(1L << 3)] 12 | [InlineData(1L << 4)] 13 | [InlineData(1L << 5)] 14 | [InlineData(1L << 6)] 15 | [InlineData(1L << 7)] 16 | [InlineData(1L << 8)] 17 | [InlineData(1L << 9)] 18 | [InlineData(1L << 10)] 19 | public async Task Test_Concurrent_Messages(long concurrency) 20 | { 21 | var (_, mediator) = Fixture.GetMediator(); 22 | var concrete = (Mediator)mediator; 23 | 24 | var id = Guid.NewGuid(); 25 | var message = new SomeRequest(id); 26 | 27 | var start = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); 28 | 29 | var threads = new Task[concurrency]; 30 | for (int i = 0; i < concurrency; i++) 31 | { 32 | threads[i] = Task.Run(Thread); 33 | } 34 | 35 | start.SetResult(); 36 | await Task.WhenAll(threads); 37 | 38 | async Task Thread() 39 | { 40 | await start.Task; 41 | 42 | const int count = 1000; 43 | for (int i = 0; i < count; i++) 44 | { 45 | var response = await concrete.Send(message); 46 | Assert.Equal(id, response.Id); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator.Tests.TestTypes; 4 | 5 | public sealed record SomeCommand(Guid Id) : ICommand; 6 | 7 | public sealed record SomeCommandWithoutResponse(Guid Id) : ICommand; 8 | 9 | public readonly struct SomeStructCommand : ICommand 10 | { 11 | public SomeStructCommand(Guid id) 12 | { 13 | Id = id; 14 | CorrelationId = Guid.NewGuid(); 15 | } 16 | 17 | public Guid Id { get; } 18 | 19 | public Guid CorrelationId { get; } 20 | } 21 | 22 | #pragma warning disable MSG0005 // MediatorGenerator message warning 23 | public sealed record SomeCommandWithoutHandler(Guid Id) : ICommand; 24 | #pragma warning restore MSG0005 // MediatorGenerator message warning 25 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeCommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Mediator.Tests.TestTypes; 7 | 8 | public sealed class SomeCommandHandler : ICommandHandler 9 | { 10 | internal static readonly ConcurrentBag Ids = new(); 11 | 12 | public ValueTask Handle(SomeCommand command, CancellationToken cancellationToken) 13 | { 14 | Ids.Add(command.Id); 15 | return new ValueTask(new SomeResponse(command.Id)); 16 | } 17 | } 18 | 19 | public sealed class SomeCommandWithoutResponseHandler : ICommandHandler 20 | { 21 | internal static readonly ConcurrentBag Ids = new(); 22 | 23 | public ValueTask Handle(SomeCommandWithoutResponse command, CancellationToken cancellationToken) 24 | { 25 | Ids.Add(command.Id); 26 | return default; 27 | } 28 | } 29 | 30 | public sealed class SomeStructCommandHandler : ICommandHandler 31 | { 32 | internal static readonly ConcurrentBag Ids = new(); 33 | internal static readonly ConcurrentBag Addresses = new(); 34 | 35 | public unsafe ValueTask Handle(SomeStructCommand command, CancellationToken cancellationToken) 36 | { 37 | Ids.Add(command.Id); 38 | var addr = *(long*)&command; 39 | Addresses.Add(addr); 40 | return default; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeNotification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator.Tests.TestTypes; 4 | 5 | public interface ISomeNotification : INotification 6 | { 7 | Guid Id { get; } 8 | } 9 | 10 | public sealed record SomeNotification(Guid Id) : ISomeNotification; 11 | 12 | public sealed record SomeOtherNotification(Guid Id) : ISomeNotification; 13 | 14 | public readonly record struct SomeStructNotification(Guid Id) : ISomeNotification; 15 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeNotificationHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Mediator.Tests.TestTypes; 7 | 8 | public sealed class SomeNotificationHandler : INotificationHandler 9 | { 10 | internal static readonly ConcurrentBag Ids = new(); 11 | internal readonly ConcurrentDictionary InstanceIds = new(); 12 | 13 | public ValueTask Handle(SomeNotification notification, CancellationToken cancellationToken) 14 | { 15 | Ids.Add(notification.Id); 16 | InstanceIds.AddOrUpdate(notification.Id, 1, (_, count) => count + 1); 17 | return default; 18 | } 19 | } 20 | 21 | public sealed class SomeStructNotificationHandler : INotificationHandler 22 | { 23 | internal static readonly ConcurrentBag Ids = new(); 24 | internal readonly ConcurrentDictionary InstanceIds = new(); 25 | 26 | public unsafe ValueTask Handle(SomeStructNotification notification, CancellationToken cancellationToken) 27 | { 28 | Ids.Add(notification.Id); 29 | InstanceIds.AddOrUpdate(notification.Id, 1, (_, count) => count + 1); 30 | return default; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeOtherNotificationHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Mediator.Tests.TestTypes; 7 | 8 | public sealed class SomeOtherNotificationHandler : INotificationHandler 9 | { 10 | internal static readonly ConcurrentBag Ids = new(); 11 | internal readonly ConcurrentDictionary InstanceIds = new(); 12 | 13 | public ValueTask Handle(SomeNotification notification, CancellationToken cancellationToken) 14 | { 15 | Ids.Add(notification.Id); 16 | InstanceIds.AddOrUpdate(notification.Id, 1, (_, count) => count + 1); 17 | return default; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator.Tests.TestTypes; 4 | 5 | public sealed record SomeQuery(Guid Id) : IQuery; 6 | 7 | #pragma warning disable MSG0005 // MediatorGenerator message warning 8 | public sealed record SomeQueryWithoutHandler(Guid Id) : IQuery; 9 | #pragma warning restore MSG0005 // MediatorGenerator message warning 10 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeQueryHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace Mediator.Tests.TestTypes; 5 | 6 | public sealed class SomeQueryHandler : IQueryHandler 7 | { 8 | public ValueTask Handle(SomeQuery Query, CancellationToken cancellationToken) => 9 | new ValueTask(new SomeResponse(Query.Id)); 10 | } 11 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator.Tests.TestTypes; 4 | 5 | public sealed record SomeRequest(Guid Id) : IRequest; 6 | 7 | public sealed record SomeRequestWithoutResponse(Guid Id) : IRequest; 8 | 9 | public sealed record SomeRequestReturningByteArray(Guid Id) : IRequest; 10 | 11 | #pragma warning disable MSG0005 // MediatorGenerator message warning 12 | public sealed record SomeRequestWithoutHandler(Guid Id) : IRequest; 13 | #pragma warning restore MSG0005 // MediatorGenerator message warning 14 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Mediator.Tests.TestTypes; 4 | 5 | public sealed record SomeResponse(Guid Id) 6 | { 7 | public int SomeStreamingData { get; set; } 8 | 9 | public bool ReturnedEarly { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeStreamingCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Mediator.Tests.TestTypes; 7 | 8 | public sealed record SomeStreamingCommand(Guid Id) : IStreamCommand; 9 | 10 | public sealed class SomeStreamingCommandHandler : IStreamCommandHandler 11 | { 12 | public async IAsyncEnumerable Handle( 13 | SomeStreamingCommand command, 14 | [EnumeratorCancellation] CancellationToken cancellationToken 15 | ) 16 | { 17 | for (int i = 0; i < 3; i++) 18 | { 19 | try 20 | { 21 | await Task.Delay(100, cancellationToken); 22 | } 23 | catch (TaskCanceledException) 24 | { 25 | yield break; 26 | } 27 | 28 | yield return new SomeResponse(command.Id); 29 | } 30 | } 31 | } 32 | 33 | #pragma warning disable MSG0005 // MediatorGenerator message warning 34 | public sealed record SomeStreamingCommandWithoutHandler(Guid Id) : IStreamCommand; 35 | #pragma warning restore MSG0005 // MediatorGenerator message warning 36 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeStreamingCommandStruct.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Mediator.Tests.TestTypes; 7 | 8 | public record struct SomeStreamingCommandStruct(Guid Id) : IStreamCommand; 9 | 10 | public sealed class SomeStreamingCommandStructHandler : IStreamCommandHandler 11 | { 12 | public async IAsyncEnumerable Handle( 13 | SomeStreamingCommandStruct command, 14 | [EnumeratorCancellation] CancellationToken cancellationToken 15 | ) 16 | { 17 | for (int i = 0; i < 3; i++) 18 | { 19 | try 20 | { 21 | await Task.Delay(100, cancellationToken); 22 | } 23 | catch (TaskCanceledException) 24 | { 25 | yield break; 26 | } 27 | 28 | yield return new SomeResponse(command.Id); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeStreamingQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Mediator.Tests.TestTypes; 7 | 8 | public sealed record SomeStreamingQuery(Guid Id) : IStreamQuery; 9 | 10 | public sealed class SomeStreamingQueryHandler : IStreamQueryHandler 11 | { 12 | public async IAsyncEnumerable Handle( 13 | SomeStreamingQuery query, 14 | [EnumeratorCancellation] CancellationToken cancellationToken 15 | ) 16 | { 17 | for (int i = 0; i < 3; i++) 18 | { 19 | try 20 | { 21 | await Task.Delay(100, cancellationToken); 22 | } 23 | catch (TaskCanceledException) 24 | { 25 | yield break; 26 | } 27 | 28 | yield return new SomeResponse(query.Id); 29 | } 30 | } 31 | } 32 | 33 | #pragma warning disable MSG0005 // MediatorGenerator message warning 34 | public sealed record SomeStreamingQueryWithoutHandler(Guid Id) : IStreamQuery; 35 | #pragma warning restore MSG0005 // MediatorGenerator message warning 36 | -------------------------------------------------------------------------------- /test/Mediator.Tests/TestTypes/SomeStreamingRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace Mediator.Tests.TestTypes; 7 | 8 | public sealed record SomeStreamingRequest(Guid Id) : IStreamRequest; 9 | 10 | public sealed class SomeStreamingRequestHandler : IStreamRequestHandler 11 | { 12 | public async IAsyncEnumerable Handle( 13 | SomeStreamingRequest request, 14 | [EnumeratorCancellation] CancellationToken cancellationToken 15 | ) 16 | { 17 | for (int i = 0; i < 3; i++) 18 | { 19 | try 20 | { 21 | await Task.Delay(100, cancellationToken); 22 | } 23 | catch (TaskCanceledException) 24 | { 25 | yield break; 26 | } 27 | 28 | yield return new SomeResponse(request.Id); 29 | } 30 | } 31 | } 32 | 33 | #pragma warning disable MSG0005 // MediatorGenerator message warning 34 | public sealed record SomeStreamingRequestWithoutHandler(Guid Id) : IStreamRequest; 35 | #pragma warning restore MSG0005 // MediatorGenerator message warning 36 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "3.0-preview.{height}", 4 | "nuGetPackageVersion": { 5 | "semVer": 2.0 6 | }, 7 | "publicReleaseRefSpec": [ 8 | "^refs/heads/main$", 9 | "^refs/heads/release/v\\d+\\.\\d+" 10 | ], 11 | "cloudBuild": { 12 | "buildNumber": { 13 | "enabled": true 14 | } 15 | }, 16 | "release": { 17 | "branchName": "release/v{version}", 18 | "firstUnstableTag": "preview" 19 | } 20 | } 21 | --------------------------------------------------------------------------------