├── NuGet.config
├── MediatR.snk
├── src
├── TestApp
│ ├── Pong.cs
│ ├── Ponged.cs
│ ├── Pinged.cs
│ ├── Ping.cs
│ ├── Jing.cs
│ ├── TestApp.csproj
│ ├── GenericHandler.cs
│ ├── JingHandler.cs
│ ├── GenericRequestPreProcessor.cs
│ ├── ConstrainedPingedHandler.cs
│ ├── GenericRequestPostProcessor.cs
│ ├── ConstrainedRequestPostProcessor.cs
│ ├── PingHandler.cs
│ ├── GenericPipelineBehavior.cs
│ ├── PingPongExceptionHandlers.cs
│ ├── Program.cs
│ ├── PingedHandler.cs
│ └── Runner.cs
└── MediatR.Extensions.Microsoft.DependencyInjection
│ ├── RequestExceptionActionProcessorStrategy.cs
│ ├── MediatrServiceConfiguration.cs
│ ├── MediatR.Extensions.Microsoft.DependencyInjection.csproj
│ ├── ServiceCollectionExtensions.cs
│ └── Registration
│ └── ServiceRegistrar.cs
├── README.md
├── assets
└── logo
│ ├── flat_128x128.png
│ ├── flat_32x32.png
│ ├── flat_64x64.png
│ ├── gradient_32x32.png
│ ├── gradient_64x64.png
│ └── gradient_128x128.png
├── test
└── MediatR.Extensions.Microsoft.DependencyInjection.Tests
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── DuplicateAssemblyResolutionTests.cs
│ ├── DerivingRequestsTests.cs
│ ├── TypeEvaluatorTests.cs
│ ├── MediatR.Extensions.Microsoft.DependencyInjection.Tests.csproj
│ ├── AssemblyResolutionTests.cs
│ ├── CustomMediatorTests.cs
│ ├── TypeResolutionTests.cs
│ ├── StreamPipelineTests.cs
│ ├── PipeLineMultiCallToConstructorTest.cs
│ ├── Handlers.cs
│ └── PipelineTests.cs
├── Directory.Build.props
├── .gitattributes
├── Push.ps1
├── .github
└── workflows
│ ├── ci.yml
│ └── release.yml
├── LICENSE
├── .vscode
├── launch.json
└── tasks.json
├── Build.ps1
├── MediatR.DI.sln
└── .gitignore
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/MediatR.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/HEAD/MediatR.snk
--------------------------------------------------------------------------------
/src/TestApp/Pong.cs:
--------------------------------------------------------------------------------
1 | namespace TestApp;
2 |
3 | public class Pong
4 | {
5 | public string? Message { get; set; }
6 | }
--------------------------------------------------------------------------------
/src/TestApp/Ponged.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace TestApp;
4 |
5 | public class Ponged : INotification
6 | {
7 |
8 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This repo is archived and the functionality is moved into the main [MediatR repository](https://github.com/jbogard/MediatR).
2 |
--------------------------------------------------------------------------------
/src/TestApp/Pinged.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace TestApp;
4 |
5 | public class Pinged : INotification
6 | {
7 |
8 | }
--------------------------------------------------------------------------------
/assets/logo/flat_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/HEAD/assets/logo/flat_128x128.png
--------------------------------------------------------------------------------
/assets/logo/flat_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/HEAD/assets/logo/flat_32x32.png
--------------------------------------------------------------------------------
/assets/logo/flat_64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/HEAD/assets/logo/flat_64x64.png
--------------------------------------------------------------------------------
/assets/logo/gradient_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/HEAD/assets/logo/gradient_32x32.png
--------------------------------------------------------------------------------
/assets/logo/gradient_64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/HEAD/assets/logo/gradient_64x64.png
--------------------------------------------------------------------------------
/assets/logo/gradient_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/HEAD/assets/logo/gradient_128x128.png
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | [assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)]
2 |
--------------------------------------------------------------------------------
/src/TestApp/Ping.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 |
3 | namespace TestApp;
4 |
5 | public class Ping : IRequest
6 | {
7 | public string? Message { get; set; }
8 | public bool Throw { get; set; }
9 | }
--------------------------------------------------------------------------------
/src/MediatR.Extensions.Microsoft.DependencyInjection/RequestExceptionActionProcessorStrategy.cs:
--------------------------------------------------------------------------------
1 | namespace MediatR;
2 |
3 | public enum RequestExceptionActionProcessorStrategy
4 | {
5 | ApplyForUnhandledExceptions,
6 | ApplyForAllExceptions
7 | }
--------------------------------------------------------------------------------
/src/TestApp/Jing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using MediatR;
7 |
8 | namespace TestApp;
9 |
10 | public class Jing : IRequest
11 | {
12 | public string? Message { get; set; }
13 | }
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | Jimmy Bogard
4 | 10.0
5 | enable
6 | $(NoWarn);1701;1702;1591
7 | true
8 | Apache-2.0
9 | v
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.doc diff=astextplain
2 | *.DOC diff=astextplain
3 | *.docx diff=astextplain
4 | *.DOCX diff=astextplain
5 | *.dot diff=astextplain
6 | *.DOT diff=astextplain
7 | *.pdf diff=astextplain
8 | *.PDF diff=astextplain
9 | *.rtf diff=astextplain
10 | *.RTF diff=astextplain
11 |
12 | *.jpg binary
13 | *.png binary
14 | *.gif binary
15 |
16 | core.eol crlf
17 |
18 | *.cs diff=csharp
19 |
20 | *.csproj merge=union
21 | *.vbproj merge=union
22 | *.fsproj merge=union
23 | *.dbproj merge=union
24 | *.sln merge=union
25 |
--------------------------------------------------------------------------------
/src/TestApp/TestApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | Exe
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/TestApp/GenericHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using MediatR;
4 |
5 | namespace TestApp;
6 |
7 | using System.IO;
8 |
9 | public class GenericHandler : INotificationHandler
10 | {
11 | private readonly TextWriter _writer;
12 |
13 | public GenericHandler(TextWriter writer)
14 | {
15 | _writer = writer;
16 | }
17 |
18 | public Task Handle(INotification notification, CancellationToken cancellationToken)
19 | {
20 | return _writer.WriteLineAsync("Got notified.");
21 | }
22 | }
--------------------------------------------------------------------------------
/src/TestApp/JingHandler.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using MediatR;
5 |
6 | namespace TestApp;
7 |
8 | public class JingHandler : AsyncRequestHandler
9 | {
10 | private readonly TextWriter _writer;
11 |
12 | public JingHandler(TextWriter writer)
13 | {
14 | _writer = writer;
15 | }
16 |
17 | protected override Task Handle(Jing request, CancellationToken cancellationToken)
18 | {
19 | return _writer.WriteLineAsync($"--- Handled Jing: {request.Message}, no Jong");
20 | }
21 | }
--------------------------------------------------------------------------------
/Push.ps1:
--------------------------------------------------------------------------------
1 | $scriptName = $MyInvocation.MyCommand.Name
2 | $artifacts = "./artifacts"
3 |
4 | if ([string]::IsNullOrEmpty($Env:NUGET_API_KEY)) {
5 | Write-Host "${scriptName}: NUGET_API_KEY is empty or not set. Skipped pushing package(s)."
6 | } else {
7 | Get-ChildItem $artifacts -Filter "*.nupkg" | ForEach-Object {
8 | Write-Host "$($scriptName): Pushing $($_.Name)"
9 | dotnet nuget push $_ --source $Env:NUGET_URL --api-key $Env:NUGET_API_KEY
10 | if ($lastexitcode -ne 0) {
11 | throw ("Exec: " + $errorMessage)
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/TestApp/GenericRequestPreProcessor.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using MediatR.Pipeline;
5 |
6 | namespace TestApp;
7 |
8 | public class GenericRequestPreProcessor : IRequestPreProcessor where TRequest : notnull
9 | {
10 | private readonly TextWriter _writer;
11 |
12 | public GenericRequestPreProcessor(TextWriter writer)
13 | {
14 | _writer = writer;
15 | }
16 |
17 | public Task Process(TRequest request, CancellationToken cancellationToken)
18 | {
19 | return _writer.WriteLineAsync("- Starting Up");
20 | }
21 | }
--------------------------------------------------------------------------------
/src/TestApp/ConstrainedPingedHandler.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using MediatR;
5 |
6 | namespace TestApp;
7 |
8 | public class ConstrainedPingedHandler : INotificationHandler
9 | where TNotification : Pinged
10 | {
11 | private readonly TextWriter _writer;
12 |
13 | public ConstrainedPingedHandler(TextWriter writer)
14 | {
15 | _writer = writer;
16 | }
17 |
18 | public Task Handle(TNotification notification, CancellationToken cancellationToken)
19 | {
20 | return _writer.WriteLineAsync("Got pinged constrained async.");
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/TestApp/GenericRequestPostProcessor.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using MediatR;
5 | using MediatR.Pipeline;
6 |
7 | namespace TestApp;
8 |
9 | public class GenericRequestPostProcessor : IRequestPostProcessor
10 | where TRequest : IRequest
11 |
12 | {
13 | private readonly TextWriter _writer;
14 |
15 | public GenericRequestPostProcessor(TextWriter writer)
16 | {
17 | _writer = writer;
18 | }
19 |
20 | public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken)
21 | {
22 | return _writer.WriteLineAsync("- All Done");
23 | }
24 | }
--------------------------------------------------------------------------------
/src/TestApp/ConstrainedRequestPostProcessor.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using MediatR;
5 | using MediatR.Pipeline;
6 |
7 | namespace TestApp;
8 |
9 | public class ConstrainedRequestPostProcessor
10 | : IRequestPostProcessor
11 | where TRequest : Ping, IRequest
12 | {
13 | private readonly TextWriter _writer;
14 |
15 | public ConstrainedRequestPostProcessor(TextWriter writer)
16 | {
17 | _writer = writer;
18 | }
19 |
20 | public Task Process(TRequest request, TResponse response, CancellationToken token)
21 | {
22 | return _writer.WriteLineAsync("- All Done with Ping");
23 | }
24 | }
--------------------------------------------------------------------------------
/src/TestApp/PingHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading;
4 | using MediatR;
5 |
6 | namespace TestApp;
7 |
8 | using System.Threading.Tasks;
9 |
10 | public class PingHandler : IRequestHandler
11 | {
12 | private readonly TextWriter _writer;
13 |
14 | public PingHandler(TextWriter writer)
15 | {
16 | _writer = writer;
17 | }
18 |
19 | public async Task Handle(Ping request, CancellationToken cancellationToken)
20 | {
21 | await _writer.WriteLineAsync($"--- Handled Ping: {request.Message}");
22 |
23 | if (request.Throw)
24 | {
25 | throw new ApplicationException("Requested to throw");
26 | }
27 |
28 | return new Pong { Message = request.Message + " Pong" };
29 | }
30 | }
--------------------------------------------------------------------------------
/src/TestApp/GenericPipelineBehavior.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using MediatR;
5 |
6 | namespace TestApp;
7 |
8 | public class GenericPipelineBehavior : IPipelineBehavior
9 | where TRequest: IRequest
10 | {
11 | private readonly TextWriter _writer;
12 |
13 | public GenericPipelineBehavior(TextWriter writer)
14 | {
15 | _writer = writer;
16 | }
17 |
18 | public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken)
19 | {
20 | await _writer.WriteLineAsync("-- Handling Request");
21 | var response = await next();
22 | await _writer.WriteLineAsync("-- Finished Request");
23 | return response;
24 | }
25 | }
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/DuplicateAssemblyResolutionTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests;
4 |
5 | using System;
6 | using System.Linq;
7 | using Shouldly;
8 | using Xunit;
9 |
10 | public class DuplicateAssemblyResolutionTests
11 | {
12 | private readonly IServiceProvider _provider;
13 |
14 | public DuplicateAssemblyResolutionTests()
15 | {
16 | IServiceCollection services = new ServiceCollection();
17 | services.AddSingleton(new Logger());
18 | services.AddMediatR(typeof(Ping), typeof(Ping));
19 | _provider = services.BuildServiceProvider();
20 | }
21 |
22 | [Fact]
23 | public void ShouldResolveNotificationHandlersOnlyOnce()
24 | {
25 | _provider.GetServices>().Count().ShouldBe(3);
26 | }
27 | }
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 | jobs:
11 | build:
12 | strategy:
13 | matrix:
14 | os: [windows-latest]
15 | fail-fast: false
16 | runs-on: windows-latest
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 | - name: Setup dotnet 6.0
23 | uses: actions/setup-dotnet@v1
24 | with:
25 | dotnet-version: '6.0.x'
26 | - name: Build and Test
27 | run: ./Build.ps1
28 | shell: pwsh
29 | - name: Push to MyGet
30 | env:
31 | NUGET_URL: https://www.myget.org/F/mediatr-ci/api/v3/index.json
32 | NUGET_API_KEY: ${{ secrets.MYGET_MEDIATR_CI_API_KEY }}
33 | run: ./Push.ps1
34 | shell: pwsh
35 | - name: Artifacts
36 | uses: actions/upload-artifact@v2
37 | with:
38 | name: artifacts
39 | path: artifacts/**/*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Jimmy Bogard
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 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*.*.*'
7 | jobs:
8 | build:
9 | strategy:
10 | matrix:
11 | os: [windows-latest]
12 | fail-fast: false
13 | runs-on: windows-latest
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v2
17 | with:
18 | fetch-depth: 0
19 | - name: Setup dotnet 6.0
20 | uses: actions/setup-dotnet@v1
21 | with:
22 | dotnet-version: '6.0.x'
23 | - name: Build and Test
24 | run: ./Build.ps1
25 | shell: pwsh
26 | - name: Push to MyGet
27 | env:
28 | NUGET_URL: https://www.myget.org/F/mediatr-ci/api/v3/index.json
29 | NUGET_API_KEY: ${{ secrets.MYGET_MEDIATR_CI_API_KEY }}
30 | run: ./Push.ps1
31 | shell: pwsh
32 | - name: Push to NuGet
33 | env:
34 | NUGET_URL: https://api.nuget.org/v3/index.json
35 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
36 | run: ./Push.ps1
37 | shell: pwsh
38 | - name: Artifacts
39 | uses: actions/upload-artifact@v2
40 | with:
41 | name: artifacts
42 | path: artifacts/**/*
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to find out which attributes exist for C# debugging
3 | // Use hover for the description of the existing attributes
4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
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}/src/TestApp/bin/Debug/netcoreapp2.1/TestApp.dll",
14 | "args": [],
15 | "cwd": "${workspaceFolder}/src/TestApp",
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 | }
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/DerivingRequestsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Shouldly;
5 | using Xunit;
6 |
7 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests;
8 |
9 | public class DerivingRequestsTests
10 | {
11 | private readonly IServiceProvider _provider;
12 | private readonly IMediator _mediator;
13 |
14 | public DerivingRequestsTests()
15 | {
16 | IServiceCollection services = new ServiceCollection();
17 | services.AddSingleton(new Logger());
18 | services.AddMediatR(typeof(Ping));
19 | _provider = services.BuildServiceProvider();
20 | _mediator = _provider.GetRequiredService();
21 | }
22 |
23 | [Fact]
24 | public async Task ShouldReturnPingPong()
25 | {
26 | Pong pong = await _mediator.Send(new Ping() { Message = "Ping" });
27 | pong.Message.ShouldBe("Ping Pong");
28 | }
29 |
30 | [Fact]
31 | public async Task ShouldReturnDerivedPingPong()
32 | {
33 | Pong pong = await _mediator.Send(new DerivedPing() { Message = "Ping" });
34 | pong.Message.ShouldBe("DerivedPing Pong");
35 | }
36 | }
--------------------------------------------------------------------------------
/Build.ps1:
--------------------------------------------------------------------------------
1 | # Taken from psake https://github.com/psake/psake
2 |
3 | <#
4 | .SYNOPSIS
5 | This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
6 | to see if an error occcured. If an error is detected then an exception is thrown.
7 | This function allows you to run command-line programs without having to
8 | explicitly check the $lastexitcode variable.
9 | .EXAMPLE
10 | exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
11 | #>
12 | function Exec
13 | {
14 | [CmdletBinding()]
15 | param(
16 | [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
17 | [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
18 | )
19 | & $cmd
20 | if ($lastexitcode -ne 0) {
21 | throw ("Exec: " + $errorMessage)
22 | }
23 | }
24 |
25 | $artifacts = ".\artifacts"
26 |
27 | if(Test-Path $artifacts) { Remove-Item $artifacts -Force -Recurse }
28 |
29 | exec { & dotnet clean -c Release }
30 |
31 | exec { & dotnet build -c Release }
32 |
33 | exec { & dotnet test -c Release -r $artifacts --no-build -l trx --verbosity=normal }
34 |
35 | exec { dotnet pack .\src\MediatR.Extensions.Microsoft.DependencyInjection -c Release -o $artifacts --no-build }
36 |
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/TypeEvaluatorTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests;
4 |
5 | using MediatR.Extensions.Microsoft.DependencyInjection.Tests.Included;
6 | using Shouldly;
7 | using System;
8 | using System.Reflection;
9 | using Xunit;
10 |
11 | public class TypeEvaluatorTests
12 | {
13 | private readonly IServiceProvider _provider;
14 |
15 | public TypeEvaluatorTests()
16 | {
17 | IServiceCollection services = new ServiceCollection();
18 | services.AddSingleton(new Logger());
19 | services.AddMediatR(new[] { typeof(Ping).GetTypeInfo().Assembly }, cfg =>
20 | {
21 | cfg.WithEvaluator(t => t.Namespace == "MediatR.Extensions.Microsoft.DependencyInjection.Tests.Included");
22 | });
23 | _provider = services.BuildServiceProvider();
24 | }
25 |
26 | [Fact]
27 | public void ShouldResolveMediator()
28 | {
29 | _provider.GetService().ShouldNotBeNull();
30 | }
31 |
32 | [Fact]
33 | public void ShouldOnlyResolveIncludedRequestHandlers()
34 | {
35 | _provider.GetService>().ShouldNotBeNull();
36 | _provider.GetService>().ShouldBeNull();
37 | }
38 | }
--------------------------------------------------------------------------------
/.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}/src/TestApp/TestApp.csproj",
11 | "/property:GenerateFullPaths=true",
12 | "/consoleloggerparameters:NoSummary"
13 | ],
14 | "problemMatcher": "$msCompile"
15 | },
16 | {
17 | "label": "publish",
18 | "command": "dotnet",
19 | "type": "process",
20 | "args": [
21 | "publish",
22 | "${workspaceFolder}/src/TestApp/TestApp.csproj",
23 | "/property:GenerateFullPaths=true",
24 | "/consoleloggerparameters:NoSummary"
25 | ],
26 | "problemMatcher": "$msCompile"
27 | },
28 | {
29 | "label": "watch",
30 | "command": "dotnet",
31 | "type": "process",
32 | "args": [
33 | "watch",
34 | "run",
35 | "${workspaceFolder}/src/TestApp/TestApp.csproj",
36 | "/property:GenerateFullPaths=true",
37 | "/consoleloggerparameters:NoSummary"
38 | ],
39 | "problemMatcher": "$msCompile"
40 | }
41 | ]
42 | }
--------------------------------------------------------------------------------
/src/TestApp/PingPongExceptionHandlers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using MediatR.Pipeline;
6 |
7 | namespace TestApp;
8 |
9 | public class PingPongExceptionHandlerForType : IRequestExceptionHandler
10 | {
11 | public Task Handle(Ping request, ApplicationException exception, RequestExceptionHandlerState state, CancellationToken cancellationToken)
12 | {
13 | state.SetHandled(new Pong { Message = exception.Message + " Handled by Type" });
14 |
15 | return Task.CompletedTask;
16 | }
17 | }
18 |
19 | public class PingPongExceptionActionForType1 : IRequestExceptionAction
20 | {
21 | private readonly TextWriter _output;
22 |
23 | public PingPongExceptionActionForType1(TextWriter output) => _output = output;
24 |
25 | public Task Execute(Ping request, ApplicationException exception, CancellationToken cancellationToken)
26 | => _output.WriteLineAsync("Logging exception 1");
27 | }
28 |
29 | public class PingPongExceptionActionForType2 : IRequestExceptionAction
30 | {
31 | private readonly TextWriter _output;
32 |
33 | public PingPongExceptionActionForType2(TextWriter output) => _output = output;
34 |
35 | public Task Execute(Ping request, ApplicationException exception, CancellationToken cancellationToken)
36 | => _output.WriteLineAsync("Logging exception 2");
37 | }
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/MediatR.Extensions.Microsoft.DependencyInjection.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | true
6 | MediatR.Extensions.Microsoft.DependencyInjection.Tests
7 | MediatR.Extensions.Microsoft.DependencyInjection.Tests
8 | true
9 | false
10 | false
11 | false
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/MediatR.Extensions.Microsoft.DependencyInjection/MediatrServiceConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace MediatR;
4 |
5 | using System;
6 |
7 | public class MediatRServiceConfiguration
8 | {
9 | public Func TypeEvaluator { get; private set; } = t => true;
10 | public Type MediatorImplementationType { get; private set; }
11 | public ServiceLifetime Lifetime { get; private set; }
12 | public RequestExceptionActionProcessorStrategy RequestExceptionActionProcessorStrategy { get; set; }
13 |
14 | public MediatRServiceConfiguration()
15 | {
16 | MediatorImplementationType = typeof(Mediator);
17 | Lifetime = ServiceLifetime.Transient;
18 | }
19 |
20 | public MediatRServiceConfiguration Using() where TMediator : IMediator
21 | {
22 | MediatorImplementationType = typeof(TMediator);
23 | return this;
24 | }
25 |
26 | public MediatRServiceConfiguration AsSingleton()
27 | {
28 | Lifetime = ServiceLifetime.Singleton;
29 | return this;
30 | }
31 |
32 | public MediatRServiceConfiguration AsScoped()
33 | {
34 | Lifetime = ServiceLifetime.Scoped;
35 | return this;
36 | }
37 |
38 | public MediatRServiceConfiguration AsTransient()
39 | {
40 | Lifetime = ServiceLifetime.Transient;
41 | return this;
42 | }
43 |
44 | public MediatRServiceConfiguration WithEvaluator(Func evaluator)
45 | {
46 | TypeEvaluator = evaluator;
47 | return this;
48 | }
49 | }
--------------------------------------------------------------------------------
/src/TestApp/Program.cs:
--------------------------------------------------------------------------------
1 | using MediatR;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using System;
4 | using System.IO;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using MediatR.Pipeline;
8 |
9 | namespace TestApp;
10 |
11 | public class Program
12 | {
13 | public static Task Main(string[] args)
14 | {
15 | var writer = new WrappingWriter(Console.Out);
16 | var mediator = BuildMediator(writer);
17 | return Runner.Run(mediator, writer, "ASP.NET Core DI");
18 | }
19 |
20 | private static IMediator BuildMediator(WrappingWriter writer)
21 | {
22 | var services = new ServiceCollection();
23 |
24 | services.AddScoped(p => p.GetRequiredService);
25 |
26 | services.AddSingleton(writer);
27 |
28 | //Pipeline
29 |
30 | //This causes a type load exception. https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/issues/12
31 | services.AddScoped(typeof(IRequestPostProcessor<,>), typeof(ConstrainedRequestPostProcessor<,>));
32 | services.AddScoped(typeof(INotificationHandler<>), typeof(ConstrainedPingedHandler<>));
33 |
34 | services.AddMediatR(typeof(Ping));
35 |
36 | services.AddScoped(typeof(IPipelineBehavior<,>), typeof(GenericPipelineBehavior<,>));
37 |
38 | foreach (var service in services)
39 | {
40 | Console.WriteLine(service.ServiceType + " - " + service.ImplementationType);
41 | }
42 |
43 | var provider = services.BuildServiceProvider();
44 |
45 | return provider.GetRequiredService();
46 | }
47 | }
--------------------------------------------------------------------------------
/src/MediatR.Extensions.Microsoft.DependencyInjection/MediatR.Extensions.Microsoft.DependencyInjection.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MediatR extensions for ASP.NET Core
5 | Copyright Jimmy Bogard
6 | netstandard2.1
7 | MediatR.Extensions.Microsoft.DependencyInjection
8 | MediatR
9 | MediatR.Extensions.Microsoft.DependencyInjection
10 | mediator;request;response;queries;commands;notifications
11 | true
12 | ..\..\MediatR.snk
13 | gradient_128x128.png
14 | v
15 | Apache-2.0
16 | true
17 | true
18 | snupkg
19 | true
20 | true
21 | true
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/AssemblyResolutionTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests;
4 |
5 | using System;
6 | using System.Linq;
7 | using System.Reflection;
8 | using Shouldly;
9 | using Xunit;
10 |
11 | public class AssemblyResolutionTests
12 | {
13 | private readonly IServiceProvider _provider;
14 |
15 | public AssemblyResolutionTests()
16 | {
17 | IServiceCollection services = new ServiceCollection();
18 | services.AddSingleton(new Logger());
19 | services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly);
20 | _provider = services.BuildServiceProvider();
21 | }
22 |
23 | [Fact]
24 | public void ShouldResolveMediator()
25 | {
26 | _provider.GetService().ShouldNotBeNull();
27 | }
28 |
29 | [Fact]
30 | public void ShouldResolveRequestHandler()
31 | {
32 | _provider.GetService>().ShouldNotBeNull();
33 | }
34 |
35 | [Fact]
36 | public void ShouldResolveInternalHandler()
37 | {
38 | _provider.GetService>().ShouldNotBeNull();
39 | }
40 |
41 | [Fact]
42 | public void ShouldResolveNotificationHandlers()
43 | {
44 | _provider.GetServices>().Count().ShouldBe(3);
45 | }
46 |
47 | [Fact]
48 | public void ShouldResolveStreamHandlers()
49 | {
50 | _provider.GetService>().ShouldNotBeNull();
51 | }
52 |
53 | [Fact]
54 | public void ShouldRequireAtLeastOneAssembly()
55 | {
56 | var services = new ServiceCollection();
57 |
58 | Action registration = () => services.AddMediatR(new Type[0]);
59 |
60 | registration.ShouldThrow();
61 | }
62 | }
--------------------------------------------------------------------------------
/src/TestApp/PingedHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using MediatR;
3 |
4 | namespace TestApp;
5 |
6 | using System.IO;
7 | using System.Threading.Tasks;
8 |
9 | public class PingedHandler : INotificationHandler
10 | {
11 | private readonly TextWriter _writer;
12 |
13 | public PingedHandler(TextWriter writer)
14 | {
15 | _writer = writer;
16 | }
17 |
18 | public Task Handle(Pinged notification, CancellationToken cancellationToken)
19 | {
20 | return _writer.WriteLineAsync("Got pinged async.");
21 | }
22 | }
23 |
24 | public class PongedHandler : INotificationHandler
25 | {
26 | private readonly TextWriter _writer;
27 |
28 | public PongedHandler(TextWriter writer)
29 | {
30 | _writer = writer;
31 | }
32 |
33 | public Task Handle(Ponged notification, CancellationToken cancellationToken)
34 | {
35 | return _writer.WriteLineAsync("Got ponged async.");
36 | }
37 | }
38 |
39 | //public class ConstrainedPingedHandler : INotificationHandler
40 | // where TNotification : Pinged
41 | //{
42 | // private readonly TextWriter _writer;
43 |
44 | // public ConstrainedPingedHandler(TextWriter writer)
45 | // {
46 | // _writer = writer;
47 | // }
48 |
49 | // public Task Handle(TNotification notification, CancellationToken cancellationToken)
50 | // {
51 | // return _writer.WriteLineAsync("Got pinged constrained async.");
52 | // }
53 | //}
54 |
55 | public class PingedAlsoHandler : INotificationHandler
56 | {
57 | private readonly TextWriter _writer;
58 |
59 | public PingedAlsoHandler(TextWriter writer)
60 | {
61 | _writer = writer;
62 | }
63 |
64 | public Task Handle(Pinged notification, CancellationToken cancellationToken)
65 | {
66 | return _writer.WriteLineAsync("Got pinged also async.");
67 | }
68 | }
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/CustomMediatorTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests;
4 |
5 | using System;
6 | using System.Linq;
7 | using Shouldly;
8 | using Xunit;
9 |
10 | public class CustomMediatorTests
11 | {
12 | private readonly IServiceProvider _provider;
13 |
14 | public CustomMediatorTests()
15 | {
16 | IServiceCollection services = new ServiceCollection();
17 | services.AddSingleton(new Logger());
18 | services.AddMediatR(cfg => cfg.Using(), typeof(CustomMediatorTests));
19 | _provider = services.BuildServiceProvider();
20 | }
21 |
22 | [Fact]
23 | public void ShouldResolveMediator()
24 | {
25 | _provider.GetService().ShouldNotBeNull();
26 | _provider.GetRequiredService().GetType().ShouldBe(typeof(MyCustomMediator));
27 | }
28 |
29 | [Fact]
30 | public void ShouldResolveRequestHandler()
31 | {
32 | _provider.GetService>().ShouldNotBeNull();
33 | }
34 |
35 | [Fact]
36 | public void ShouldResolveNotificationHandlers()
37 | {
38 | _provider.GetServices>().Count().ShouldBe(3);
39 | }
40 |
41 | [Fact]
42 | public void Can_Call_AddMediatr_multiple_times()
43 | {
44 | IServiceCollection services = new ServiceCollection();
45 | services.AddSingleton(new Logger());
46 | services.AddMediatR(cfg => cfg.Using(), typeof(CustomMediatorTests));
47 |
48 | // Call AddMediatr again, this should NOT override our custom mediatr (With MS DI, last registration wins)
49 | services.AddMediatR(typeof(CustomMediatorTests));
50 |
51 | var provider = services.BuildServiceProvider();
52 | var mediator = provider.GetRequiredService();
53 | mediator.GetType().ShouldBe(typeof(MyCustomMediator));
54 | }
55 | }
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/TypeResolutionTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests;
4 |
5 | using System;
6 | using System.Linq;
7 | using System.Reflection;
8 | using Shouldly;
9 | using Xunit;
10 |
11 | public class TypeResolutionTests
12 | {
13 | private readonly IServiceProvider _provider;
14 |
15 | public TypeResolutionTests()
16 | {
17 | IServiceCollection services = new ServiceCollection();
18 | services.AddSingleton(new Logger());
19 | services.AddMediatR(typeof(Ping));
20 | _provider = services.BuildServiceProvider();
21 | }
22 |
23 | [Fact]
24 | public void ShouldResolveMediator()
25 | {
26 | _provider.GetService().ShouldNotBeNull();
27 | }
28 |
29 | [Fact]
30 | public void ShouldResolveSender()
31 | {
32 | _provider.GetService().ShouldNotBeNull();
33 | }
34 |
35 | [Fact]
36 | public void ShouldResolvePublisher()
37 | {
38 | _provider.GetService().ShouldNotBeNull();
39 | }
40 |
41 | [Fact]
42 | public void ShouldResolveRequestHandler()
43 | {
44 | _provider.GetService>().ShouldNotBeNull();
45 | }
46 |
47 | [Fact]
48 | public void ShouldResolveVoidRequestHandler()
49 | {
50 | _provider.GetService>().ShouldNotBeNull();
51 | }
52 |
53 | [Fact]
54 | public void ShouldResolveNotificationHandlers()
55 | {
56 | _provider.GetServices>().Count().ShouldBe(3);
57 | }
58 |
59 | [Fact]
60 | public void ShouldResolveFirstDuplicateHandler()
61 | {
62 | _provider.GetService>().ShouldNotBeNull();
63 | _provider.GetService>()
64 | .ShouldBeAssignableTo();
65 | }
66 |
67 | [Fact]
68 | public void ShouldResolveIgnoreSecondDuplicateHandler()
69 | {
70 | _provider.GetServices>().Count().ShouldBe(1);
71 | }
72 | }
--------------------------------------------------------------------------------
/MediatR.DI.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.28803.156
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{1082ED20-13D1-444F-BAFD-17EFB9152981}"
7 | ProjectSection(SolutionItems) = preProject
8 | Build.ps1 = Build.ps1
9 | .github\workflows\ci.yml = .github\workflows\ci.yml
10 | Directory.Build.props = Directory.Build.props
11 | Push.ps1 = Push.ps1
12 | README.md = README.md
13 | .github\workflows\release.yml = .github\workflows\release.yml
14 | EndProjectSection
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Extensions.Microsoft.DependencyInjection", "src\MediatR.Extensions.Microsoft.DependencyInjection\MediatR.Extensions.Microsoft.DependencyInjection.csproj", "{CD3BCC44-44C0-4A5A-9041-34B558A9FBF2}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediatR.Extensions.Microsoft.DependencyInjection.Tests", "test\MediatR.Extensions.Microsoft.DependencyInjection.Tests\MediatR.Extensions.Microsoft.DependencyInjection.Tests.csproj", "{A93A7F85-292A-4130-891D-4307D3F60C30}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "src\TestApp\TestApp.csproj", "{DE95F633-80B5-4248-A594-7FB357C8DAC9}"
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Release|Any CPU = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {CD3BCC44-44C0-4A5A-9041-34B558A9FBF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {CD3BCC44-44C0-4A5A-9041-34B558A9FBF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {CD3BCC44-44C0-4A5A-9041-34B558A9FBF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {CD3BCC44-44C0-4A5A-9041-34B558A9FBF2}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {A93A7F85-292A-4130-891D-4307D3F60C30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {A93A7F85-292A-4130-891D-4307D3F60C30}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {A93A7F85-292A-4130-891D-4307D3F60C30}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {A93A7F85-292A-4130-891D-4307D3F60C30}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {DE95F633-80B5-4248-A594-7FB357C8DAC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {DE95F633-80B5-4248-A594-7FB357C8DAC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {DE95F633-80B5-4248-A594-7FB357C8DAC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {DE95F633-80B5-4248-A594-7FB357C8DAC9}.Release|Any CPU.Build.0 = Release|Any CPU
40 | EndGlobalSection
41 | GlobalSection(SolutionProperties) = preSolution
42 | HideSolutionNode = FALSE
43 | EndGlobalSection
44 | GlobalSection(ExtensibilityGlobals) = postSolution
45 | SolutionGuid = {531488EE-5812-470F-A050-73EB27DFF03B}
46 | EndGlobalSection
47 | EndGlobal
48 |
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/StreamPipelineTests.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests;
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Reflection;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 | using Pipeline;
13 | using Shouldly;
14 | using Xunit;
15 |
16 | public class StreamPipelineTests
17 | {
18 | public class OuterBehavior : IStreamPipelineBehavior
19 | {
20 | private readonly Logger _output;
21 |
22 | public OuterBehavior(Logger output)
23 | {
24 | _output = output;
25 | }
26 |
27 | public async IAsyncEnumerable Handle(StreamPing request, StreamHandlerDelegate next, [EnumeratorCancellation] CancellationToken cancellationToken)
28 | {
29 | _output.Messages.Add("Outer before");
30 | await foreach (var response in next().WithCancellation(cancellationToken))
31 | {
32 | yield return response;
33 | }
34 | _output.Messages.Add("Outer after");
35 | }
36 | }
37 |
38 | public class InnerBehavior : IStreamPipelineBehavior
39 | {
40 | private readonly Logger _output;
41 |
42 | public InnerBehavior(Logger output)
43 | {
44 | _output = output;
45 | }
46 |
47 | public async IAsyncEnumerable Handle(StreamPing request, StreamHandlerDelegate next, [EnumeratorCancellation] CancellationToken cancellationToken)
48 | {
49 | _output.Messages.Add("Inner before");
50 | await foreach (var response in next().WithCancellation(cancellationToken))
51 | {
52 | yield return response;
53 | }
54 | _output.Messages.Add("Inner after");
55 | }
56 | }
57 |
58 | [Fact]
59 | public async Task Should_wrap_with_behavior()
60 | {
61 | var output = new Logger();
62 | IServiceCollection services = new ServiceCollection();
63 | services.AddSingleton(output);
64 | services.AddTransient, OuterBehavior>();
65 | services.AddTransient, InnerBehavior>();
66 | services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly);
67 | var provider = services.BuildServiceProvider();
68 |
69 | var mediator = provider.GetRequiredService();
70 |
71 | var stream = mediator.CreateStream(new StreamPing { Message = "Ping" });
72 |
73 | await foreach (var response in stream)
74 | {
75 | response.Message.ShouldBe("Ping Pang");
76 | }
77 |
78 | output.Messages.ShouldBe(new[]
79 | {
80 | "Outer before",
81 | "Inner before",
82 | "Handler",
83 | "Inner after",
84 | "Outer after"
85 | });
86 | }
87 | }
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/PipeLineMultiCallToConstructorTest.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests;
5 |
6 | using System.Reflection;
7 | using System.Threading.Tasks;
8 | using Shouldly;
9 | using Xunit;
10 |
11 | public class PipelineMultiCallToConstructorTests
12 | {
13 | public class ConstructorTestBehavior : IPipelineBehavior
14 | where TRequest : IRequest
15 | {
16 | private readonly Logger _output;
17 |
18 | public ConstructorTestBehavior(Logger output) => _output = output;
19 |
20 | public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken)
21 | {
22 | _output.Messages.Add("ConstructorTestBehavior before");
23 | var response = await next();
24 | _output.Messages.Add("ConstructorTestBehavior after");
25 |
26 | return response;
27 | }
28 | }
29 |
30 | public class ConstructorTestRequest : IRequest
31 | {
32 | public string? Message { get; set; }
33 | }
34 |
35 | public class ConstructorTestResponse
36 | {
37 | public string? Message { get; set; }
38 | }
39 |
40 | public class ConstructorTestHandler : IRequestHandler
41 | {
42 |
43 | private static volatile object _lockObject = new();
44 | private readonly Logger _logger;
45 | private static int _constructorCallCount;
46 |
47 | public static int ConstructorCallCount => _constructorCallCount;
48 |
49 | public static void ResetCallCount()
50 | {
51 | lock (_lockObject)
52 | {
53 | _constructorCallCount = 0;
54 | }
55 | }
56 |
57 | public ConstructorTestHandler(Logger logger)
58 | {
59 | _logger = logger;
60 | lock (_lockObject)
61 | {
62 | _constructorCallCount++;
63 | }
64 | }
65 |
66 | public Task Handle(ConstructorTestRequest request, CancellationToken cancellationToken)
67 | {
68 | _logger.Messages.Add("Handler");
69 | return Task.FromResult(new ConstructorTestResponse { Message = request.Message + " ConstructorPong" });
70 | }
71 | }
72 |
73 | [Fact]
74 | public async Task Should_not_call_constructor_multiple_times_when_using_a_pipeline()
75 | {
76 | ConstructorTestHandler.ResetCallCount();
77 | ConstructorTestHandler.ConstructorCallCount.ShouldBe(0);
78 |
79 | var output = new Logger();
80 | IServiceCollection services = new ServiceCollection();
81 |
82 | services.AddSingleton(output);
83 | services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ConstructorTestBehavior<,>));
84 | services.AddMediatR(typeof(Ping).GetTypeInfo().Assembly);
85 | var provider = services.BuildServiceProvider();
86 |
87 | var mediator = provider.GetRequiredService();
88 |
89 | var response = await mediator.Send(new ConstructorTestRequest { Message = "ConstructorPing" });
90 |
91 | response.Message.ShouldBe("ConstructorPing ConstructorPong");
92 |
93 | output.Messages.ShouldBe(new[]
94 | {
95 | "ConstructorTestBehavior before",
96 | "First pre processor",
97 | "Next pre processor",
98 | "Handler",
99 | "First post processor",
100 | "Next post processor",
101 | "ConstructorTestBehavior after"
102 | });
103 | ConstructorTestHandler.ConstructorCallCount.ShouldBe(1);
104 | }
105 | }
--------------------------------------------------------------------------------
/src/MediatR.Extensions.Microsoft.DependencyInjection/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using MediatR.Pipeline;
6 | using MediatR.Registration;
7 | using Microsoft.Extensions.DependencyInjection;
8 |
9 | namespace MediatR;
10 |
11 | ///
12 | /// Extensions to scan for MediatR handlers and registers them.
13 | /// - Scans for any handler interface implementations and registers them as
14 | /// - Scans for any and implementations and registers them as transient instances
15 | /// Registers and as transient instances
16 | /// After calling AddMediatR you can use the container to resolve an instance.
17 | /// This does not scan for any instances including and .
18 | /// To register behaviors, use the with the open generic or closed generic types.
19 | ///
20 | public static class ServiceCollectionExtensions
21 | {
22 | ///
23 | /// Registers handlers and mediator types from the specified assemblies
24 | ///
25 | /// Service collection
26 | /// Assemblies to scan
27 | /// Service collection
28 | public static IServiceCollection AddMediatR(this IServiceCollection services, params Assembly[] assemblies)
29 | => services.AddMediatR(assemblies, configuration: null);
30 |
31 | ///
32 | /// Registers handlers and mediator types from the specified assemblies
33 | ///
34 | /// Service collection
35 | /// Assemblies to scan
36 | /// The action used to configure the options
37 | /// Service collection
38 | public static IServiceCollection AddMediatR(this IServiceCollection services, Action? configuration, params Assembly[] assemblies)
39 | => services.AddMediatR(assemblies, configuration);
40 |
41 | ///
42 | /// Registers handlers and mediator types from the specified assemblies
43 | ///
44 | /// Service collection
45 | /// Assemblies to scan
46 | /// The action used to configure the options
47 | /// Service collection
48 | public static IServiceCollection AddMediatR(this IServiceCollection services, IEnumerable assemblies, Action? configuration)
49 | {
50 | if (!assemblies.Any())
51 | {
52 | throw new ArgumentException("No assemblies found to scan. Supply at least one assembly to scan for handlers.");
53 | }
54 | var serviceConfig = new MediatRServiceConfiguration();
55 |
56 | configuration?.Invoke(serviceConfig);
57 |
58 | ServiceRegistrar.AddRequiredServices(services, serviceConfig);
59 |
60 | ServiceRegistrar.AddMediatRClasses(services, assemblies, serviceConfig);
61 |
62 | return services;
63 | }
64 |
65 | ///
66 | /// Registers handlers and mediator types from the assemblies that contain the specified types
67 | ///
68 | ///
69 | ///
70 | /// Service collection
71 | public static IServiceCollection AddMediatR(this IServiceCollection services, params Type[] handlerAssemblyMarkerTypes)
72 | => services.AddMediatR(handlerAssemblyMarkerTypes, configuration: null);
73 |
74 | ///
75 | /// Registers handlers and mediator types from the assemblies that contain the specified types
76 | ///
77 | ///
78 | ///
79 | /// The action used to configure the options
80 | /// Service collection
81 | public static IServiceCollection AddMediatR(this IServiceCollection services, Action? configuration, params Type[] handlerAssemblyMarkerTypes)
82 | => services.AddMediatR(handlerAssemblyMarkerTypes, configuration);
83 |
84 | ///
85 | /// Registers handlers and mediator types from the assemblies that contain the specified types
86 | ///
87 | ///
88 | ///
89 | /// The action used to configure the options
90 | /// Service collection
91 | public static IServiceCollection AddMediatR(this IServiceCollection services, IEnumerable handlerAssemblyMarkerTypes, Action? configuration)
92 | => services.AddMediatR(handlerAssemblyMarkerTypes.Select(t => t.GetTypeInfo().Assembly), configuration);
93 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
--------------------------------------------------------------------------------
/src/TestApp/Runner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 | using MediatR;
5 |
6 | namespace TestApp;
7 |
8 | using System.IO;
9 | using System.Threading.Tasks;
10 |
11 | public static class Runner
12 | {
13 | public static async Task Run(IMediator mediator, WrappingWriter writer, string projectName)
14 | {
15 | await writer.WriteLineAsync("===============");
16 | await writer.WriteLineAsync(projectName);
17 | await writer.WriteLineAsync("===============");
18 |
19 | await writer.WriteLineAsync("Sending Ping...");
20 | var pong = await mediator.Send(new Ping { Message = "Ping" });
21 | await writer.WriteLineAsync("Received: " + pong.Message);
22 |
23 | await writer.WriteLineAsync("Publishing Pinged...");
24 | await mediator.Publish(new Pinged());
25 |
26 | await writer.WriteLineAsync("Publishing Ponged...");
27 | var failedPong = false;
28 | try
29 | {
30 | await mediator.Publish(new Ponged());
31 | }
32 | catch (Exception e)
33 | {
34 | failedPong = true;
35 | await writer.WriteLineAsync(e.ToString());
36 | }
37 |
38 | bool failedJing = false;
39 | await writer.WriteLineAsync("Sending Jing...");
40 | try
41 | {
42 | await mediator.Send(new Jing { Message = "Jing" });
43 | }
44 | catch (Exception e)
45 | {
46 | failedJing = true;
47 | await writer.WriteLineAsync(e.ToString());
48 | }
49 |
50 | await writer.WriteLineAsync("---------------");
51 | var contents = writer.Contents;
52 | var order = new[] {
53 | contents.IndexOf("- Starting Up", StringComparison.OrdinalIgnoreCase),
54 | contents.IndexOf("-- Handling Request", StringComparison.OrdinalIgnoreCase),
55 | contents.IndexOf("--- Handled Ping", StringComparison.OrdinalIgnoreCase),
56 | contents.IndexOf("-- Finished Request", StringComparison.OrdinalIgnoreCase),
57 | contents.IndexOf("- All Done", StringComparison.OrdinalIgnoreCase),
58 | contents.IndexOf("- All Done with Ping", StringComparison.OrdinalIgnoreCase),
59 | };
60 |
61 | var results = new RunResults
62 | {
63 | RequestHandlers = contents.Contains("--- Handled Ping:"),
64 | VoidRequestsHandlers = contents.Contains("--- Handled Jing:"),
65 | PipelineBehaviors = contents.Contains("-- Handling Request"),
66 | RequestPreProcessors = contents.Contains("- Starting Up"),
67 | RequestPostProcessors = contents.Contains("- All Done"),
68 | ConstrainedGenericBehaviors = contents.Contains("- All Done with Ping") && !failedJing,
69 | OrderedPipelineBehaviors = order.SequenceEqual(order.OrderBy(i => i)),
70 | NotificationHandler = contents.Contains("Got pinged async"),
71 | MultipleNotificationHandlers = contents.Contains("Got pinged async") && contents.Contains("Got pinged also async"),
72 | ConstrainedGenericNotificationHandler = contents.Contains("Got pinged constrained async") && !failedPong,
73 | CovariantNotificationHandler = contents.Contains("Got notified")
74 | };
75 |
76 | await writer.WriteLineAsync($"Request Handler...................{(results.RequestHandlers ? "Y" : "N")}");
77 | await writer.WriteLineAsync($"Void Request Handler..............{(results.VoidRequestsHandlers ? "Y" : "N")}");
78 | await writer.WriteLineAsync($"Pipeline Behavior.................{(results.PipelineBehaviors ? "Y" : "N")}");
79 | await writer.WriteLineAsync($"Pre-Processor.....................{(results.RequestPreProcessors ? "Y" : "N")}");
80 | await writer.WriteLineAsync($"Post-Processor....................{(results.RequestPostProcessors ? "Y" : "N")}");
81 | await writer.WriteLineAsync($"Constrained Post-Processor........{(results.ConstrainedGenericBehaviors ? "Y" : "N")}");
82 | await writer.WriteLineAsync($"Ordered Behaviors.................{(results.OrderedPipelineBehaviors ? "Y" : "N")}");
83 | await writer.WriteLineAsync($"Notification Handler..............{(results.NotificationHandler ? "Y" : "N")}");
84 | await writer.WriteLineAsync($"Notification Handlers.............{(results.MultipleNotificationHandlers ? "Y" : "N")}");
85 | await writer.WriteLineAsync($"Constrained Notification Handler..{(results.ConstrainedGenericNotificationHandler ? "Y" : "N")}");
86 | await writer.WriteLineAsync($"Covariant Notification Handler....{(results.CovariantNotificationHandler ? "Y" : "N")}");
87 | }
88 | }
89 |
90 | public class RunResults
91 | {
92 | public bool RequestHandlers { get; init; }
93 | public bool VoidRequestsHandlers { get; init; }
94 | public bool PipelineBehaviors { get; init; }
95 | public bool RequestPreProcessors { get; init; }
96 | public bool RequestPostProcessors { get; init; }
97 | public bool OrderedPipelineBehaviors { get; init; }
98 | public bool ConstrainedGenericBehaviors { get; init; }
99 | public bool NotificationHandler { get; init; }
100 | public bool MultipleNotificationHandlers { get; init; }
101 | public bool CovariantNotificationHandler { get; init; }
102 | public bool ConstrainedGenericNotificationHandler { get; init; }
103 | }
104 |
105 | public class WrappingWriter : TextWriter
106 | {
107 | private readonly TextWriter _innerWriter;
108 | private readonly StringBuilder _stringWriter = new();
109 |
110 | public WrappingWriter(TextWriter innerWriter)
111 | {
112 | _innerWriter = innerWriter;
113 | }
114 |
115 | public override void Write(char value)
116 | {
117 | _stringWriter.Append(value);
118 | _innerWriter.Write(value);
119 | }
120 |
121 | public override Task WriteLineAsync(string? value)
122 | {
123 | _stringWriter.AppendLine(value);
124 | return _innerWriter.WriteLineAsync(value);
125 | }
126 |
127 | public override Encoding Encoding => _innerWriter.Encoding;
128 |
129 | public string Contents => _stringWriter.ToString();
130 | }
--------------------------------------------------------------------------------
/test/MediatR.Extensions.Microsoft.DependencyInjection.Tests/Handlers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 |
4 | namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests
5 | {
6 | using System.Collections.Generic;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | public class Ping : IRequest
11 | {
12 | public string? Message { get; init; }
13 | public Action? ThrowAction { get; init; }
14 | }
15 |
16 | public class DerivedPing : Ping
17 | {
18 | }
19 |
20 | public class Pong
21 | {
22 | public string? Message { get; init; }
23 | }
24 |
25 | public class Zing : IRequest
26 | {
27 | public string? Message { get; init; }
28 | }
29 |
30 | public class Zong
31 | {
32 | public string? Message { get; init; }
33 | }
34 |
35 | public class Ding : IRequest
36 | {
37 | public string? Message { get; init; }
38 | }
39 |
40 | public class Pinged : INotification
41 | {
42 |
43 | }
44 |
45 | class InternalPing : IRequest { }
46 |
47 | public class StreamPing : IStreamRequest
48 | {
49 | public string? Message { get; init; }
50 | }
51 |
52 | public class GenericHandler : INotificationHandler
53 | {
54 | public Task Handle(INotification notification, CancellationToken cancellationToken)
55 | {
56 | return Task.FromResult(0);
57 | }
58 | }
59 |
60 | public class DingAsyncHandler : IRequestHandler
61 | {
62 | public Task Handle(Ding message, CancellationToken cancellationToken) => Unit.Task;
63 | }
64 |
65 | public class PingedHandler : INotificationHandler
66 | {
67 | public Task Handle(Pinged notification, CancellationToken cancellationToken)
68 | {
69 | return Task.CompletedTask;
70 | }
71 | }
72 |
73 | public class PingedAlsoHandler : INotificationHandler
74 | {
75 | public Task Handle(Pinged notification, CancellationToken cancellationToken)
76 | {
77 | return Task.CompletedTask;
78 | }
79 | }
80 |
81 | public class Logger
82 | {
83 | public IList Messages { get; } = new List();
84 | }
85 |
86 | public class PingHandler : IRequestHandler
87 | {
88 | private readonly Logger _logger;
89 |
90 | public PingHandler(Logger logger)
91 | {
92 | _logger = logger;
93 | }
94 | public Task Handle(Ping message, CancellationToken cancellationToken)
95 | {
96 | _logger.Messages.Add("Handler");
97 |
98 | message.ThrowAction?.Invoke(message);
99 |
100 | return Task.FromResult(new Pong { Message = message.Message + " Pong" });
101 | }
102 | }
103 |
104 | public class DerivedPingHandler : IRequestHandler
105 | {
106 | private readonly Logger _logger;
107 |
108 | public DerivedPingHandler(Logger logger)
109 | {
110 | _logger = logger;
111 | }
112 | public Task Handle(DerivedPing message, CancellationToken cancellationToken)
113 | {
114 | _logger.Messages.Add("Handler");
115 | return Task.FromResult(new Pong { Message = $"Derived{message.Message} Pong" });
116 | }
117 | }
118 |
119 | public class ZingHandler : IRequestHandler
120 | {
121 | private readonly Logger _output;
122 |
123 | public ZingHandler(Logger output)
124 | {
125 | _output = output;
126 | }
127 | public Task Handle(Zing message, CancellationToken cancellationToken)
128 | {
129 | _output.Messages.Add("Handler");
130 | return Task.FromResult(new Zong { Message = message.Message + " Zong" });
131 | }
132 | }
133 |
134 | public class PingStreamHandler : IStreamRequestHandler
135 | {
136 | private readonly Logger _output;
137 |
138 | public PingStreamHandler(Logger output)
139 | {
140 | _output = output;
141 | }
142 | public async IAsyncEnumerable Handle(StreamPing request, [EnumeratorCancellation] CancellationToken cancellationToken)
143 | {
144 | _output.Messages.Add("Handler");
145 | yield return await Task.Run(() => new Pong { Message = request.Message + " Pang" }, cancellationToken);
146 | }
147 | }
148 |
149 |
150 | public class DuplicateTest : IRequest { }
151 | public class DuplicateHandler1 : IRequestHandler
152 | {
153 | public Task Handle(DuplicateTest message, CancellationToken cancellationToken)
154 | {
155 | return Task.FromResult(nameof(DuplicateHandler1));
156 | }
157 | }
158 |
159 | public class DuplicateHandler2 : IRequestHandler
160 | {
161 | public Task Handle(DuplicateTest message, CancellationToken cancellationToken)
162 | {
163 | return Task.FromResult(nameof(DuplicateHandler2));
164 | }
165 | }
166 |
167 | class InternalPingHandler : IRequestHandler
168 | {
169 | public Task Handle(InternalPing request, CancellationToken cancellationToken) => Unit.Task;
170 | }
171 |
172 | class MyCustomMediator : IMediator
173 | {
174 | public Task