Repository: LuckyPennySoftware/MediatR
Branch: main
Commit: f72aef8d786f
Files: 182
Total size: 438.7 KB
Directory structure:
gitextract_64igt8ai/
├── .editorconfig
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── ci.yml
│ ├── release.yml
│ └── triage-issues.yml
├── .gitignore
├── Build.ps1
├── BuildContracts.ps1
├── Directory.Build.props
├── LICENSE.md
├── MediatR.slnx
├── MediatR.snk
├── NuGet.Config
├── Push.ps1
├── README.md
├── samples/
│ ├── MediatR.Examples/
│ │ ├── ConstrainedRequestPostProcessor.cs
│ │ ├── ExceptionHandler/
│ │ │ ├── Exceptions.cs
│ │ │ ├── ExceptionsHandlers.cs
│ │ │ ├── ExceptionsHandlersOverrides.cs
│ │ │ ├── Handlers.cs
│ │ │ ├── LogExceptionAction.cs
│ │ │ ├── Requests.cs
│ │ │ └── RequestsOverrides.cs
│ │ ├── GenericHandler.cs
│ │ ├── GenericPipelineBehavior.cs
│ │ ├── GenericRequestPostProcessor.cs
│ │ ├── GenericRequestPreProcessor.cs
│ │ ├── Jing.cs
│ │ ├── JingHandler.cs
│ │ ├── MediatR.Examples.csproj
│ │ ├── Ping.cs
│ │ ├── PingHandler.cs
│ │ ├── Pinged.cs
│ │ ├── PingedHandler.cs
│ │ ├── Pong.cs
│ │ ├── Ponged.cs
│ │ ├── Runner.cs
│ │ └── Streams/
│ │ ├── GenericStreamPipelineBehavior.cs
│ │ ├── Sing.cs
│ │ ├── SingHandler.cs
│ │ └── Song.cs
│ ├── MediatR.Examples.AspNetCore/
│ │ ├── MediatR.Examples.AspNetCore.csproj
│ │ └── Program.cs
│ ├── MediatR.Examples.Autofac/
│ │ ├── MediatR.Examples.Autofac.csproj
│ │ └── Program.cs
│ ├── MediatR.Examples.DryIoc/
│ │ ├── MediatR.Examples.DryIoc.csproj
│ │ └── Program.cs
│ ├── MediatR.Examples.Lamar/
│ │ ├── MediatR.Examples.Lamar.csproj
│ │ └── Program.cs
│ ├── MediatR.Examples.LightInject/
│ │ ├── MediatR.Examples.LightInject.csproj
│ │ └── Program.cs
│ ├── MediatR.Examples.PublishStrategies/
│ │ ├── AsyncPingedHandler.cs
│ │ ├── CustomMediator.cs
│ │ ├── MediatR.Examples.PublishStrategies.csproj
│ │ ├── Program.cs
│ │ ├── PublishStrategy.cs
│ │ ├── Publisher.cs
│ │ └── SyncPingedHandler.cs
│ ├── MediatR.Examples.SimpleInjector/
│ │ ├── MediatR.Examples.SimpleInjector.csproj
│ │ └── Program.cs
│ ├── MediatR.Examples.Stashbox/
│ │ ├── MediatR.Examples.Stashbox.csproj
│ │ └── Program.cs
│ └── MediatR.Examples.Windsor/
│ ├── ContravariantFilter.cs
│ ├── MediatR.Examples.Windsor.csproj
│ └── Program.cs
├── src/
│ ├── MediatR/
│ │ ├── Entities/
│ │ │ └── OpenBehavior.cs
│ │ ├── IMediator.cs
│ │ ├── INotificationHandler.cs
│ │ ├── INotificationPublisher.cs
│ │ ├── IPipelineBehavior.cs
│ │ ├── IPublisher.cs
│ │ ├── IRequestHandler.cs
│ │ ├── ISender.cs
│ │ ├── IStreamPipelineBehavior.cs
│ │ ├── IStreamRequestHandler.cs
│ │ ├── Internal/
│ │ │ ├── HandlersOrderer.cs
│ │ │ └── ObjectDetails.cs
│ │ ├── Licensing/
│ │ │ ├── BuildInfo.cs
│ │ │ ├── Edition.cs
│ │ │ ├── License.cs
│ │ │ ├── LicenseAccessor.cs
│ │ │ ├── LicenseValidator.cs
│ │ │ └── ProductType.cs
│ │ ├── MediatR.csproj
│ │ ├── Mediator.cs
│ │ ├── MicrosoftExtensionsDI/
│ │ │ ├── MediatRServiceCollectionExtensions.cs
│ │ │ ├── MediatrServiceConfiguration.cs
│ │ │ └── RequestExceptionActionProcessorStrategy.cs
│ │ ├── NotificationHandlerExecutor.cs
│ │ ├── NotificationPublishers/
│ │ │ ├── ForeachAwaitPublisher.cs
│ │ │ └── TaskWhenAllPublisher.cs
│ │ ├── Pipeline/
│ │ │ ├── IRequestExceptionAction.cs
│ │ │ ├── IRequestExceptionHandler.cs
│ │ │ ├── IRequestPostProcessor.cs
│ │ │ ├── IRequestPreProcessor.cs
│ │ │ ├── RequestExceptionActionProcessorBehavior.cs
│ │ │ ├── RequestExceptionHandlerState.cs
│ │ │ ├── RequestExceptionProcessorBehavior.cs
│ │ │ ├── RequestPostProcessorBehavior.cs
│ │ │ └── RequestPreProcessorBehavior.cs
│ │ ├── Registration/
│ │ │ └── ServiceRegistrar.cs
│ │ ├── TypeForwardings.cs
│ │ ├── Wrappers/
│ │ │ ├── NotificationHandlerWrapper.cs
│ │ │ ├── RequestHandlerWrapper.cs
│ │ │ └── StreamRequestHandlerWrapper.cs
│ │ └── license.txt
│ └── MediatR.Contracts/
│ ├── INotification.cs
│ ├── IRequest.cs
│ ├── IStreamRequest.cs
│ ├── MediatR.Contracts.csproj
│ └── Unit.cs
└── test/
├── MediatR.Benchmarks/
│ ├── Benchmarks.cs
│ ├── DotTraceDiagnoser.cs
│ ├── GenericPipelineBehavior.cs
│ ├── GenericRequestPostProcessor.cs
│ ├── GenericRequestPreProcessor.cs
│ ├── MediatR.Benchmarks.csproj
│ ├── Ping.cs
│ ├── Pinged.cs
│ └── Program.cs
├── MediatR.DependencyInjectionTests/
│ ├── Abstractions/
│ │ ├── BaseAssemblyResolutionTests.cs
│ │ └── BaseServiceProviderFixture.cs
│ ├── AutoFacDependencyInjectionTests.cs
│ ├── Contracts/
│ │ ├── Notifications/
│ │ │ └── Ding.cs
│ │ ├── Requests/
│ │ │ ├── InternalPing.cs
│ │ │ ├── InternalVoidPing.cs
│ │ │ ├── PrivatePing.cs
│ │ │ ├── PrivateVoidPing.cs
│ │ │ ├── PublicPing.cs
│ │ │ └── PublicVoidPing.cs
│ │ ├── Responses/
│ │ │ └── Pong.cs
│ │ └── StreamRequests/
│ │ ├── InternalZing.cs
│ │ ├── PrivateZing.cs
│ │ └── PublicZing.cs
│ ├── DryIocDependencyInjectionTests.cs
│ ├── LamarDependencyInjectionTests.cs
│ ├── LightInjectDependencyInjectionTests.cs
│ ├── MediatR.DependencyInjectionTests.csproj
│ ├── MicrosoftDependencyInjectionTests.cs
│ ├── Providers/
│ │ ├── AutoFacServiceProviderFixture.cs
│ │ ├── DryIocServiceProviderFixture.cs
│ │ ├── LamarServiceProviderFixture.cs
│ │ ├── LightInjectServiceProviderFixture.cs
│ │ ├── MicrosoftServiceProviderFixture.cs
│ │ └── StashBoxServiceProviderFixture.cs
│ ├── StashBoxDependencyInjectionTests.cs
│ └── Usings.cs
└── MediatR.Tests/
├── CreateStreamTests.cs
├── ExceptionTests.cs
├── GenericRequestHandlerTests.cs
├── GenericTypeConstraintsTests.cs
├── GlobalUsings.cs
├── Licensing/
│ └── LicenseValidatorTests.cs
├── MediatR.Tests.csproj
├── MicrosoftExtensionsDI/
│ ├── AssemblyResolutionTests.cs
│ ├── BaseGenericRequestHandlerTests.cs
│ ├── CustomMediatorTests.cs
│ ├── DerivingRequestsTests.cs
│ ├── DuplicateAssemblyResolutionTests.cs
│ ├── Handlers.cs
│ ├── Issue1118Tests.cs
│ ├── NotificationPublisherTests.cs
│ ├── PipeLineMultiCallToConstructorTest.cs
│ ├── PipelineTests.cs
│ ├── StreamPipelineTests.cs
│ ├── TypeEvaluatorTests.cs
│ └── TypeResolutionTests.cs
├── NotificationHandlerTests.cs
├── NotificationPublisherTests.cs
├── Pipeline/
│ ├── RequestExceptionActionTests.cs
│ ├── RequestExceptionHandlerTests.cs
│ ├── RequestPostProcessorTests.cs
│ ├── RequestPreProcessorTests.cs
│ └── Streams/
│ └── StreamPipelineBehaviorTests.cs
├── PipelineTests.cs
├── PublishTests.cs
├── SendTests.cs
├── SendVoidInterfaceTests.cs
├── ServiceFactoryTests.cs
├── StreamPipelineTests.cs
├── TestContainer.cs
└── UnitTests.cs
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root=true
; EditorConfig helps developers define and maintain consistent
; coding styles between different editors and IDEs.
; For more visit http://editorconfig.org.
; Choose between lf or rf on "end_of_line" property
[*.proto]
indent_style=tab
indent_size=tab
tab_width=4
[*.{asax,ascx,aspx,cs,cshtml,css,htm,html,js,jsx,master,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}]
indent_style=space
indent_size=4
tab_width=4
[*.{appxmanifest,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
indent_style=space
indent_size=2
tab_width=2
[*]
# Standard properties
end_of_line=native
insert_final_newline=false
# Microsoft .NET properties
csharp_indent_braces=false
csharp_indent_switch_labels=true
csharp_new_line_before_catch=true
csharp_new_line_before_else=true
csharp_new_line_before_finally=true
csharp_new_line_before_members_in_object_initializers=false
csharp_new_line_before_open_brace=all
csharp_new_line_between_query_expression_clauses=true
csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
csharp_preserve_single_line_blocks=true
csharp_space_after_cast=true
csharp_space_after_colon_in_inheritance_clause=true
csharp_space_after_comma=true
csharp_space_after_dot=false
csharp_space_after_keywords_in_control_flow_statements=true
csharp_space_after_semicolon_in_for_statement=true
csharp_space_around_binary_operators=before_and_after
csharp_space_before_colon_in_inheritance_clause=true
csharp_space_before_comma=false
csharp_space_before_dot=false
csharp_space_before_open_square_brackets=false
csharp_space_before_semicolon_in_for_statement=false
csharp_space_between_empty_square_brackets=false
csharp_space_between_method_call_empty_parameter_list_parentheses=false
csharp_space_between_method_call_name_and_opening_parenthesis=false
csharp_space_between_method_call_parameter_list_parentheses=false
csharp_space_between_method_declaration_empty_parameter_list_parentheses=false
csharp_space_between_method_declaration_name_and_open_parenthesis=false
csharp_space_between_method_declaration_parameter_list_parentheses=false
csharp_space_between_parentheses=false
csharp_space_between_square_brackets=false
csharp_style_expression_bodied_accessors=true:suggestion
csharp_style_expression_bodied_constructors=true:suggestion
csharp_style_expression_bodied_methods=true:suggestion
csharp_style_expression_bodied_properties=true:suggestion
csharp_style_var_elsewhere=true:hint
csharp_style_var_for_built_in_types=true:hint
csharp_style_var_when_type_is_apparent=true:hint
csharp_using_directive_placement=outside_namespace:silent
dotnet_style_predefined_type_for_locals_parameters_members=true:hint
dotnet_style_predefined_type_for_member_access=true:hint
dotnet_style_qualification_for_event=false:hint
dotnet_style_qualification_for_field=false:hint
dotnet_style_qualification_for_method=false:hint
dotnet_style_qualification_for_property=false:hint
dotnet_style_require_accessibility_modifiers=for_non_interface_members:hint
================================================
FILE: .gitattributes
================================================
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.jpg binary
*.png binary
*.gif binary
core.eol crlf
*.cs diff=csharp
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
*.sln merge=union
*.slnx merge=union
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
workflow_dispatch:
push:
branches:
- main
pull_request:
env:
DOTNET_NOLOGO: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
MINVERBUILDMETADATA: build.${{ github.run_id }}.${{ github.run_attempt}}
permissions:
id-token: write
contents: read
checks: write
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4.2.0
with:
fetch-depth: 0
filter: tree:0
- name: Setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
- name: Install NuGetKeyVaultSignTool
run: dotnet tool install --global NuGetKeyVaultSignTool
- name: Build and Test
run: ./Build.ps1
shell: pwsh
- name: Report Test Results
uses: dorny/test-reporter@v1.9.1
if: success() || failure()
with:
name: Test Results (Windows)
path: '**/TestResults/**/*.trx'
reporter: dotnet-trx
- name: Get package version
id: version
shell: pwsh
run: |
$pkg = Get-ChildItem ./artifacts -Filter "MediatR.*.nupkg" | Select-Object -First 1
$version = $pkg.BaseName -replace '^MediatR\.', ''
"version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
- name: Generate SBOM
shell: pwsh
run: |
dotnet tool install --global Microsoft.Sbom.DotNetTool
sbom-tool generate `
-b ./artifacts `
-bc ./src/MediatR `
-pn MediatR `
-pv ${{ steps.version.outputs.version }} `
-ps "Lucky Penny Software LLC" `
-nsb https://mediatr.io
- name: Push to MyGet
if: github.ref == 'refs/heads/main'
env:
NUGET_URL: https://f.feedz.io/lucky-penny-software/mediatr/nuget/index.json
NUGET_API_KEY: ${{ secrets.FEEDZIO_ACCESS_TOKEN }}
run: ./Push.ps1
shell: pwsh
- name: Artifacts
uses: actions/upload-artifact@v4
with:
name: artifacts
path: artifacts/**/*
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags:
- '*.*.*'
env:
DOTNET_NOLOGO: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
MINVERBUILDMETADATA: build.${{ github.run_id }}.${{ github.run_attempt}}
permissions:
id-token: write
contents: read
checks: write
jobs:
build:
strategy:
matrix:
os: [windows-2025]
fail-fast: false
runs-on: windows-2025
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
filter: tree:0
- name: Azure Login via OIDC
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
- name: Install NuGetKeyVaultSignTool
run: dotnet tool install --global NuGetKeyVaultSignTool
- name: Build and Test
run: ./Build.ps1
shell: pwsh
- name: Report Test Results
uses: dorny/test-reporter@v1.9.1
if: always()
with:
name: Test Results (Windows)
path: '**/TestResults/**/*.trx'
reporter: dotnet-trx
- name: Get package version
id: version
shell: pwsh
run: |
$pkg = Get-ChildItem ./artifacts -Filter "MediatR.*.nupkg" | Select-Object -First 1
$version = $pkg.BaseName -replace '^MediatR\.', ''
"version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
- name: Generate SBOM
shell: pwsh
run: |
dotnet tool install --global Microsoft.Sbom.DotNetTool
sbom-tool generate `
-b ./artifacts `
-bc ./src/MediatR `
-pn MediatR `
-pv ${{ steps.version.outputs.version }} `
-ps "Lucky Penny Software LLC" `
-nsb https://mediatr.io
- name: Sign packages
run: |-
foreach ($f in Get-ChildItem "./artifacts" -Filter "*.nupkg") {
NuGetKeyVaultSignTool sign $f.FullName --file-digest sha256 --timestamp-rfc3161 http://timestamp.digicert.com --azure-key-vault-managed-identity --azure-key-vault-url ${{ secrets.AZURE_KEYVAULT_URI }} --azure-key-vault-certificate ${{ secrets.CODESIGN_CERT_NAME }}
}
- name: Push to MyGet
env:
NUGET_URL: https://f.feedz.io/lucky-penny-software/mediatr/nuget/index.json
NUGET_API_KEY: ${{ secrets.FEEDZIO_ACCESS_TOKEN }}
run: ./Push.ps1
shell: pwsh
- name: Push to NuGet
env:
NUGET_URL: https://api.nuget.org/v3/index.json
NUGET_API_KEY: ${{ secrets.MEDIATR_NUGET_API_KEY }}
run: ./Push.ps1
shell: pwsh
- name: Artifacts
uses: actions/upload-artifact@v4
with:
name: artifacts
path: artifacts/**/*
================================================
FILE: .github/workflows/triage-issues.yml
================================================
# https://github.com/actions/stale
name: "Stale issue & PR handler"
on:
workflow_dispatch:
schedule:
- cron: "0 12 * * *"
env:
ISSUES_DAYS_BEFORE_CLOSE: 14
PR_DAYS_BEFORE_CLOSE: 14
ISSUES_DAYS_BEFORE_STALE: 60
PR_DAYS_BEFORE_STALE: 28
jobs:
issues:
name: "Close stale issues and PRs"
runs-on: "ubuntu-latest"
steps:
- uses: "actions/stale@v6.0.0"
with:
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open ${{ env.ISSUES_DAYS_BEFORE_STALE }} days with no activity. Remove stale label or comment or this will be closed in ${{ env.ISSUES_DAYS_BEFORE_CLOSE }} days."
close-issue-message: 'This issue was closed because it has been stalled for ${{ env.ISSUES_DAYS_BEFORE_CLOSE }} days with no activity.'
days-before-close: "${{ env.ISSUES_DAYS_BEFORE_CLOSE }}"
days-before-stale: "${{ env.ISSUES_DAYS_BEFORE_STALE }}"
exempt-issue-assignees: true
exempt-issue-labels: 'awaiting-approval,work-in-progress,up-for-grabs'
stale-pr-label: "stale"
stale-pr-message: 'This PR is stale because it has been open ${{ env.PR_DAYS_BEFORE_STALE }} days with no activity. Remove stale label or comment or this will be closed in ${{ env.PR_DAYS_BEFORE_CLOSE }} days.'
close-pr-message: 'This PR was closed because it has been stalled for ${{ env.PR_DAYS_BEFORE_CLOSE }} days with no activity.'
days-before-pr-close: "${{ env.PR_DAYS_BEFORE_CLOSE }}"
days-before-pr-stale: "${{ env.PR_DAYS_BEFORE_STALE }}"
exempt-all-pr-assignees: true
exempt-pr-labels: 'awaiting-approval,work-in-progress'
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
artifacts/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
*.ncrunchsolution
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Project Rider
*.iml
.idea
# Apple
.DS_Store
================================================
FILE: Build.ps1
================================================
# Taken from psake https://github.com/psake/psake
<#
.SYNOPSIS
This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
to see if an error occcured. If an error is detected then an exception is thrown.
This function allows you to run command-line programs without having to
explicitly check the $lastexitcode variable.
.EXAMPLE
exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
#>
function Exec
{
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
)
& $cmd
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}
$artifacts = ".\artifacts"
if(Test-Path $artifacts) { Remove-Item $artifacts -Force -Recurse }
exec { & dotnet clean -c Release }
exec { & dotnet build -c Release }
exec { & dotnet test -c Release --no-build -l trx --verbosity=normal }
exec { & dotnet pack .\src\MediatR\MediatR.csproj -c Release -o $artifacts --no-build }
================================================
FILE: BuildContracts.ps1
================================================
# Taken from psake https://github.com/psake/psake
<#
.SYNOPSIS
This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
to see if an error occcured. If an error is detected then an exception is thrown.
This function allows you to run command-line programs without having to
explicitly check the $lastexitcode variable.
.EXAMPLE
exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
#>
function Exec
{
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
)
& $cmd
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}
$artifacts = ".\artifacts"
$contracts = ".\src\MediatR.Contracts\MediatR.Contracts.csproj"
if(Test-Path $artifacts) { Remove-Item $artifacts -Force -Recurse }
exec { & dotnet clean $contracts -c Release }
exec { & dotnet build $contracts -c Release -p:ContinuousIntegrationBuild=true }
exec { & dotnet pack $contracts -c Release -o $artifacts --no-build }
================================================
FILE: Directory.Build.props
================================================
$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::get_OSX())))
$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::get_Windows())))
13.0
$(NoWarn);CS1701;CS1702;CS1591;NU1900
true
================================================
FILE: LICENSE.md
================================================
By accessing code under the [Lucky Penny Software GitHub Organization](https://github.com/LuckyPennySoftware) (Lucky Penny Software) here, you are agreeing to the following licensing terms.
If you do not agree to these terms, do not access Lucky Penny Software code.
Your license to Lucky Penny Software source code and/or binaries is governed by the Reciprocal Public License 1.5 (RPL1.5) license as described here:
https://opensource.org/license/rpl-1-5/
If you do not wish to release the source of software you build using Lucky Penny Software source code and/or binaries under the terms above, you may use Lucky Penny Software source code and/or binaries under the License Agreement described here:
https://luckypennysoftware.com/license
================================================
FILE: MediatR.slnx
================================================
================================================
FILE: NuGet.Config
================================================
================================================
FILE: Push.ps1
================================================
$scriptName = $MyInvocation.MyCommand.Name
$artifacts = "./artifacts"
if ([string]::IsNullOrEmpty($Env:NUGET_API_KEY)) {
Write-Host "${scriptName}: NUGET_API_KEY is empty or not set. Skipped pushing package(s)."
} else {
Get-ChildItem $artifacts -Filter "*.nupkg" | ForEach-Object {
Write-Host "$($scriptName): Pushing $($_.Name)"
dotnet nuget push $_ --source $Env:NUGET_URL --api-key $Env:NUGET_API_KEY --skip-duplicate
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}
}
================================================
FILE: README.md
================================================
MediatR
=======

[](https://www.nuget.org/packages/mediatr)
[](https://www.nuget.org/packages/mediatr)
[](https://myget.org/gallery/mediatr-ci)
Simple mediator implementation in .NET
In-process messaging with no dependencies.
Supports request/response, commands, queries, notifications and events, synchronous and async with intelligent dispatching via C# generic variance.
Examples in the [wiki](https://github.com/LuckyPennySoftware/MediatR/wiki).
### Installing MediatR
You should install [MediatR with NuGet](https://www.nuget.org/packages/MediatR):
Install-Package MediatR
Or via the .NET Core command line interface:
dotnet add package MediatR
Either commands, from Package Manager Console or .NET Core CLI, will download and install MediatR and all required dependencies.
### Using Contracts-Only Package
To reference only the contracts for MediatR, which includes:
- `IRequest` (including generic variants)
- `INotification`
- `IStreamRequest`
Add a package reference to [MediatR.Contracts](https://www.nuget.org/packages/MediatR.Contracts)
This package is useful in scenarios where your MediatR contracts are in a separate assembly/project from handlers. Example scenarios include:
- API contracts
- GRPC contracts
- Blazor
### Registering with `IServiceCollection`
MediatR supports `Microsoft.Extensions.DependencyInjection.Abstractions` directly. To register various MediatR services and handlers:
```
services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining());
```
or with an assembly:
```
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Startup).Assembly));
```
This registers:
- `IMediator` as transient
- `ISender` as transient
- `IPublisher` as transient
- `IRequestHandler<,>` concrete implementations as transient
- `IRequestHandler<>` concrete implementations as transient
- `INotificationHandler<>` concrete implementations as transient
- `IStreamRequestHandler<>` concrete implementations as transient
- `IRequestExceptionHandler<,,>` concrete implementations as transient
- `IRequestExceptionAction<,>)` concrete implementations as transient
This also registers open generic implementations for:
- `INotificationHandler<>`
- `IRequestExceptionHandler<,,>`
- `IRequestExceptionAction<,>`
To register behaviors, stream behaviors, pre/post processors:
```csharp
services.AddMediatR(cfg => {
cfg.RegisterServicesFromAssembly(typeof(Startup).Assembly);
cfg.AddBehavior();
cfg.AddStreamBehavior();
cfg.AddRequestPreProcessor();
cfg.AddRequestPostProcessor();
cfg.AddOpenBehavior(typeof(GenericBehavior<,>));
});
```
With additional methods for open generics and overloads for explicit service types.
### Setting the license key
You can set the license key when registering MediatR:
```csharp
services.AddMediatR(cfg =>
{
cfg.LicenseKey = "";
})
```
Or if not using Microsoft.Extensions.DependencyInjection:
```csharp
Mediator.LicenseKey = "";
```
> [!TIP]
> The license key does not need to be set on client applications (such as Blazor WASM).
> Turn off the license warning by configuring logging in your logging start configuration:
> `builder.Logging.AddFilter("LuckyPennySoftware.MediatR.License", LogLevel.None);`
You can register for your license key at [MediatR.io](https://mediatr.io)
================================================
FILE: samples/MediatR.Examples/ConstrainedRequestPostProcessor.cs
================================================
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediatR.Pipeline;
namespace MediatR.Examples;
public class ConstrainedRequestPostProcessor
: IRequestPostProcessor
where TRequest : Ping
{
private readonly TextWriter _writer;
public ConstrainedRequestPostProcessor(TextWriter writer)
{
_writer = writer;
}
public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync("- All Done with Ping");
}
}
================================================
FILE: samples/MediatR.Examples/ExceptionHandler/Exceptions.cs
================================================
using System;
namespace MediatR.Examples.ExceptionHandler;
public class ConnectionException : Exception { }
public class ForbiddenException : ConnectionException { }
public class ResourceNotFoundException : ConnectionException { }
public class ServerException : Exception { }
================================================
FILE: samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs
================================================
using MediatR.Pipeline;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples.ExceptionHandler;
public class CommonExceptionHandler : IRequestExceptionHandler
{
private readonly TextWriter _writer;
public CommonExceptionHandler(TextWriter writer) => _writer = writer;
public async Task Handle(PingResource request,
Exception exception,
RequestExceptionHandlerState state,
CancellationToken cancellationToken)
{
// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(CommonExceptionHandler).FullName}'").ConfigureAwait(false);
state.SetHandled(new Pong());
}
}
public class ConnectionExceptionHandler : IRequestExceptionHandler
{
private readonly TextWriter _writer;
public ConnectionExceptionHandler(TextWriter writer) => _writer = writer;
public async Task Handle(PingResource request,
ConnectionException exception,
RequestExceptionHandlerState state,
CancellationToken cancellationToken)
{
// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ConnectionExceptionHandler).FullName}'").ConfigureAwait(false);
state.SetHandled(new Pong());
}
}
public class AccessDeniedExceptionHandler : IRequestExceptionHandler
{
private readonly TextWriter _writer;
public AccessDeniedExceptionHandler(TextWriter writer) => _writer = writer;
public async Task Handle(PingResource request,
ForbiddenException exception,
RequestExceptionHandlerState state,
CancellationToken cancellationToken)
{
// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(AccessDeniedExceptionHandler).FullName}'").ConfigureAwait(false);
state.SetHandled(new Pong());
}
}
public class ServerExceptionHandler : IRequestExceptionHandler
{
private readonly TextWriter _writer;
public ServerExceptionHandler(TextWriter writer) => _writer = writer;
public virtual async Task Handle(PingNewResource request,
ServerException exception,
RequestExceptionHandlerState state,
CancellationToken cancellationToken)
{
// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ServerExceptionHandler).FullName}'").ConfigureAwait(false);
state.SetHandled(new Pong());
}
}
================================================
FILE: samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlersOverrides.cs
================================================
using MediatR.Pipeline;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples.ExceptionHandler.Overrides;
public class CommonExceptionHandler : IRequestExceptionHandler
{
private readonly TextWriter _writer;
public CommonExceptionHandler(TextWriter writer) => _writer = writer;
public async Task Handle(PingResourceTimeout request,
Exception exception,
RequestExceptionHandlerState state,
CancellationToken cancellationToken)
{
// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(CommonExceptionHandler).FullName}'").ConfigureAwait(false);
state.SetHandled(new Pong());
}
}
public class ServerExceptionHandler : ExceptionHandler.ServerExceptionHandler
{
private readonly TextWriter _writer;
public ServerExceptionHandler(TextWriter writer) : base(writer) => _writer = writer;
public override async Task Handle(PingNewResource request,
ServerException exception,
RequestExceptionHandlerState state,
CancellationToken cancellationToken)
{
// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ServerExceptionHandler).FullName}'").ConfigureAwait(false);
state.SetHandled(new Pong());
}
}
================================================
FILE: samples/MediatR.Examples/ExceptionHandler/Handlers.cs
================================================
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples.ExceptionHandler;
public class PingResourceHandler : IRequestHandler
{
private readonly TextWriter _writer;
public PingResourceHandler(TextWriter writer) => _writer = writer;
public Task Handle(PingResource request, CancellationToken cancellationToken)
{
throw new ResourceNotFoundException();
}
}
public class PingNewResourceHandler : IRequestHandler
{
private readonly TextWriter _writer;
public PingNewResourceHandler(TextWriter writer) => _writer = writer;
public Task Handle(PingNewResource request, CancellationToken cancellationToken)
{
throw new ServerException();
}
}
public class PingResourceTimeoutHandler : IRequestHandler
{
private readonly TextWriter _writer;
public PingResourceTimeoutHandler(TextWriter writer) => _writer = writer;
public Task Handle(PingResourceTimeout request, CancellationToken cancellationToken)
{
throw new TaskCanceledException();
}
}
public class PingResourceTimeoutOverrideHandler : IRequestHandler
{
private readonly TextWriter _writer;
public PingResourceTimeoutOverrideHandler(TextWriter writer) => _writer = writer;
public Task Handle(Overrides.PingResourceTimeout request, CancellationToken cancellationToken)
{
throw new TaskCanceledException();
}
}
public class PingProtectedResourceHandler : IRequestHandler
{
private readonly TextWriter _writer;
public PingProtectedResourceHandler(TextWriter writer) => _writer = writer;
public Task Handle(PingProtectedResource request, CancellationToken cancellationToken)
{
throw new ForbiddenException();
}
}
================================================
FILE: samples/MediatR.Examples/ExceptionHandler/LogExceptionAction.cs
================================================
using MediatR.Pipeline;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples.ExceptionHandler;
public class LogExceptionAction : IRequestExceptionAction
{
private readonly TextWriter _writer;
public LogExceptionAction(TextWriter writer) => _writer = writer;
public Task Execute(Ping request, Exception exception, CancellationToken cancellationToken)
=> _writer.WriteLineAsync($"--- Exception: '{exception.GetType().FullName}'");
}
================================================
FILE: samples/MediatR.Examples/ExceptionHandler/Requests.cs
================================================
namespace MediatR.Examples.ExceptionHandler;
public class PingResource : Ping { }
public class PingNewResource : Ping { }
public class PingResourceTimeout : PingResource { }
public class PingProtectedResource : PingResource { }
================================================
FILE: samples/MediatR.Examples/ExceptionHandler/RequestsOverrides.cs
================================================
namespace MediatR.Examples.ExceptionHandler.Overrides;
public class PingResourceTimeout : ExceptionHandler.PingResourceTimeout { }
================================================
FILE: samples/MediatR.Examples/GenericHandler.cs
================================================
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples;
public class GenericHandler : INotificationHandler
{
private readonly TextWriter _writer;
public GenericHandler(TextWriter writer)
{
_writer = writer;
}
public Task Handle(INotification notification, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync("Got notified.");
}
}
================================================
FILE: samples/MediatR.Examples/GenericPipelineBehavior.cs
================================================
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples;
public class GenericPipelineBehavior : IPipelineBehavior
{
private readonly TextWriter _writer;
public GenericPipelineBehavior(TextWriter writer)
{
_writer = writer;
}
public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken)
{
await _writer.WriteLineAsync("-- Handling Request");
var response = await next();
await _writer.WriteLineAsync("-- Finished Request");
return response;
}
}
================================================
FILE: samples/MediatR.Examples/GenericRequestPostProcessor.cs
================================================
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediatR.Pipeline;
namespace MediatR.Examples;
public class GenericRequestPostProcessor : IRequestPostProcessor
{
private readonly TextWriter _writer;
public GenericRequestPostProcessor(TextWriter writer)
{
_writer = writer;
}
public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync("- All Done");
}
}
================================================
FILE: samples/MediatR.Examples/GenericRequestPreProcessor.cs
================================================
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediatR.Pipeline;
namespace MediatR.Examples;
public class GenericRequestPreProcessor : IRequestPreProcessor
{
private readonly TextWriter _writer;
public GenericRequestPreProcessor(TextWriter writer)
{
_writer = writer;
}
public Task Process(TRequest request, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync("- Starting Up");
}
}
================================================
FILE: samples/MediatR.Examples/Jing.cs
================================================
namespace MediatR.Examples;
public class Jing : IRequest
{
public string Message { get; set; }
}
================================================
FILE: samples/MediatR.Examples/JingHandler.cs
================================================
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples;
public class JingHandler : IRequestHandler
{
private readonly TextWriter _writer;
public JingHandler(TextWriter writer)
{
_writer = writer;
}
public Task Handle(Jing request, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync($"--- Handled Jing: {request.Message}, no Jong");
}
}
================================================
FILE: samples/MediatR.Examples/MediatR.Examples.csproj
================================================
net10.0
================================================
FILE: samples/MediatR.Examples/Ping.cs
================================================
namespace MediatR.Examples;
public class Ping : IRequest
{
public string Message { get; set; }
}
================================================
FILE: samples/MediatR.Examples/PingHandler.cs
================================================
using System.IO;
using System.Threading;
namespace MediatR.Examples;
using System.Threading.Tasks;
public class PingHandler : IRequestHandler
{
private readonly TextWriter _writer;
public PingHandler(TextWriter writer)
{
_writer = writer;
}
public async Task Handle(Ping request, CancellationToken cancellationToken)
{
await _writer.WriteLineAsync($"--- Handled Ping: {request.Message}");
return new Pong { Message = request.Message + " Pong" };
}
}
================================================
FILE: samples/MediatR.Examples/Pinged.cs
================================================
namespace MediatR.Examples;
public class Pinged : INotification
{
}
================================================
FILE: samples/MediatR.Examples/PingedHandler.cs
================================================
using System.Threading;
namespace MediatR.Examples;
using System.IO;
using System.Threading.Tasks;
public class PingedHandler : INotificationHandler
{
private readonly TextWriter _writer;
public PingedHandler(TextWriter writer)
{
_writer = writer;
}
public Task Handle(Pinged notification, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync("Got pinged async.");
}
}
public class PongedHandler : INotificationHandler
{
private readonly TextWriter _writer;
public PongedHandler(TextWriter writer)
{
_writer = writer;
}
public Task Handle(Ponged notification, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync("Got ponged async.");
}
}
public class ConstrainedPingedHandler : INotificationHandler
where TNotification : Pinged
{
private readonly TextWriter _writer;
public ConstrainedPingedHandler(TextWriter writer)
{
_writer = writer;
}
public Task Handle(TNotification notification, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync("Got pinged constrained async.");
}
}
public class PingedAlsoHandler : INotificationHandler
{
private readonly TextWriter _writer;
public PingedAlsoHandler(TextWriter writer)
{
_writer = writer;
}
public Task Handle(Pinged notification, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync("Got pinged also async.");
}
}
================================================
FILE: samples/MediatR.Examples/Pong.cs
================================================
namespace MediatR.Examples;
public class Pong
{
public string Message { get; set; }
}
================================================
FILE: samples/MediatR.Examples/Ponged.cs
================================================
namespace MediatR.Examples;
public class Ponged : INotification
{
}
================================================
FILE: samples/MediatR.Examples/Runner.cs
================================================
using System;
using System.Linq;
using System.Text;
namespace MediatR.Examples;
using MediatR.Examples.ExceptionHandler;
using System.IO;
using System.Threading.Tasks;
public static class Runner
{
public static async Task Run(IMediator mediator, WrappingWriter writer, string projectName, bool testStreams = false)
{
await writer.WriteLineAsync("===============");
await writer.WriteLineAsync(projectName);
await writer.WriteLineAsync("===============");
await writer.WriteLineAsync();
await writer.WriteLineAsync("Sending Ping...");
var pong = await mediator.Send(new Ping { Message = "Ping" });
await writer.WriteLineAsync("Received: " + pong.Message);
await writer.WriteLineAsync();
await writer.WriteLineAsync("Publishing Pinged...");
await mediator.Publish(new Pinged());
await writer.WriteLineAsync();
await writer.WriteLineAsync("Publishing Ponged...");
var failedPong = false;
try
{
await mediator.Publish(new Ponged());
}
catch (Exception e)
{
failedPong = true;
await writer.WriteLineAsync(e.ToString());
}
await writer.WriteLineAsync();
var failedJing = false;
await writer.WriteLineAsync("Sending Jing...");
try
{
await mediator.Send(new Jing { Message = "Jing" });
}
catch (Exception e)
{
failedJing = true;
await writer.WriteLineAsync(e.ToString());
}
await writer.WriteLineAsync();
bool failedSing = false;
if (testStreams)
{
await writer.WriteLineAsync("Sending Sing...");
try
{
int i = 0;
await foreach (Song s in mediator.CreateStream(new Sing { Message = "Sing" }))
{
if (i == 0) {
failedSing = !(s.Message.Contains("Singing do"));
}
else if (i == 1)
{
failedSing = !(s.Message.Contains("Singing re"));
}
else if (i == 2)
{
failedSing = !(s.Message.Contains("Singing mi"));
}
else if (i == 3)
{
failedSing = !(s.Message.Contains("Singing fa"));
}
else if (i == 4)
{
failedSing = !(s.Message.Contains("Singing so"));
}
else if (i == 5)
{
failedSing = !(s.Message.Contains("Singing la"));
}
else if (i == 6)
{
failedSing = !(s.Message.Contains("Singing ti"));
}
else if (i == 7)
{
failedSing = !(s.Message.Contains("Singing do"));
}
failedSing = failedSing || (++i) > 10;
}
}
catch (Exception e)
{
failedSing = true;
await writer.WriteLineAsync(e.ToString());
}
await writer.WriteLineAsync();
}
var isHandlerForSameExceptionWorks = await IsHandlerForSameExceptionWorks(mediator, writer).ConfigureAwait(false);
var isHandlerForBaseExceptionWorks = await IsHandlerForBaseExceptionWorks(mediator, writer).ConfigureAwait(false);
var isHandlerForLessSpecificExceptionWorks = await IsHandlerForLessSpecificExceptionWorks(mediator, writer).ConfigureAwait(false);
var isPreferredHandlerForBaseExceptionWorks = await IsPreferredHandlerForBaseExceptionWorks(mediator, writer).ConfigureAwait(false);
var isOverriddenHandlerForBaseExceptionWorks = await IsOverriddenHandlerForBaseExceptionWorks(mediator, writer).ConfigureAwait(false);
await writer.WriteLineAsync("---------------");
var contents = writer.Contents;
var order = new[] {
contents.IndexOf("- Starting Up", StringComparison.OrdinalIgnoreCase),
contents.IndexOf("-- Handling Request", StringComparison.OrdinalIgnoreCase),
contents.IndexOf("--- Handled Ping", StringComparison.OrdinalIgnoreCase),
contents.IndexOf("-- Finished Request", StringComparison.OrdinalIgnoreCase),
contents.IndexOf("- All Done", StringComparison.OrdinalIgnoreCase),
contents.IndexOf("- All Done with Ping", StringComparison.OrdinalIgnoreCase),
};
var streamOrder = new[] {
contents.IndexOf("-- Handling StreamRequest", StringComparison.OrdinalIgnoreCase),
contents.IndexOf("--- Handled Sing: Sing, Song", StringComparison.OrdinalIgnoreCase),
contents.IndexOf("-- Finished StreamRequest", StringComparison.OrdinalIgnoreCase),
};
var results = new RunResults
{
RequestHandlers = contents.Contains("--- Handled Ping:"),
VoidRequestsHandlers = contents.Contains("--- Handled Jing:"),
PipelineBehaviors = contents.Contains("-- Handling Request"),
RequestPreProcessors = contents.Contains("- Starting Up"),
RequestPostProcessors = contents.Contains("- All Done"),
ConstrainedGenericBehaviors = contents.Contains("- All Done with Ping") && !failedJing,
OrderedPipelineBehaviors = order.SequenceEqual(order.OrderBy(i => i)),
NotificationHandler = contents.Contains("Got pinged async"),
MultipleNotificationHandlers = contents.Contains("Got pinged async") && contents.Contains("Got pinged also async"),
ConstrainedGenericNotificationHandler = contents.Contains("Got pinged constrained async") && !failedPong,
CovariantNotificationHandler = contents.Contains("Got notified"),
HandlerForSameException = isHandlerForSameExceptionWorks,
HandlerForBaseException = isHandlerForBaseExceptionWorks,
HandlerForLessSpecificException = isHandlerForLessSpecificExceptionWorks,
PreferredHandlerForBaseException = isPreferredHandlerForBaseExceptionWorks,
OverriddenHandlerForBaseException = isOverriddenHandlerForBaseExceptionWorks,
// Streams
StreamRequestHandlers = contents.Contains("--- Handled Sing: Sing, Song") && !failedSing,
StreamPipelineBehaviors = contents.Contains("-- Handling StreamRequest"),
StreamOrderedPipelineBehaviors = streamOrder.SequenceEqual(streamOrder.OrderBy(i => i))
};
await writer.WriteLineAsync($"Request Handler....................................................{(results.RequestHandlers ? "Y" : "N")}");
await writer.WriteLineAsync($"Void Request Handler...............................................{(results.VoidRequestsHandlers ? "Y" : "N")}");
await writer.WriteLineAsync($"Pipeline Behavior..................................................{(results.PipelineBehaviors ? "Y" : "N")}");
await writer.WriteLineAsync($"Pre-Processor......................................................{(results.RequestPreProcessors ? "Y" : "N")}");
await writer.WriteLineAsync($"Post-Processor.....................................................{(results.RequestPostProcessors ? "Y" : "N")}");
await writer.WriteLineAsync($"Constrained Post-Processor.........................................{(results.ConstrainedGenericBehaviors ? "Y" : "N")}");
await writer.WriteLineAsync($"Ordered Behaviors..................................................{(results.OrderedPipelineBehaviors ? "Y" : "N")}");
await writer.WriteLineAsync($"Notification Handler...............................................{(results.NotificationHandler ? "Y" : "N")}");
await writer.WriteLineAsync($"Notification Handlers..............................................{(results.MultipleNotificationHandlers ? "Y" : "N")}");
await writer.WriteLineAsync($"Constrained Notification Handler...................................{(results.ConstrainedGenericNotificationHandler ? "Y" : "N")}");
await writer.WriteLineAsync($"Covariant Notification Handler.....................................{(results.CovariantNotificationHandler ? "Y" : "N")}");
await writer.WriteLineAsync($"Handler for inherited request with same exception used.............{(results.HandlerForSameException ? "Y" : "N")}");
await writer.WriteLineAsync($"Handler for inherited request with base exception used.............{(results.HandlerForBaseException ? "Y" : "N")}");
await writer.WriteLineAsync($"Handler for request with less specific exception used by priority..{(results.HandlerForLessSpecificException ? "Y" : "N")}");
await writer.WriteLineAsync($"Preferred handler for inherited request with base exception used...{(results.PreferredHandlerForBaseException ? "Y" : "N")}");
await writer.WriteLineAsync($"Overridden handler for inherited request with same exception used..{(results.OverriddenHandlerForBaseException ? "Y" : "N")}");
if (testStreams)
{
await writer.WriteLineAsync($"Stream Request Handler.............................................{(results.StreamRequestHandlers ? "Y" : "N")}");
await writer.WriteLineAsync($"Stream Pipeline Behavior...........................................{(results.StreamPipelineBehaviors ? "Y" : "N")}");
await writer.WriteLineAsync($"Stream Ordered Behaviors...........................................{(results.StreamOrderedPipelineBehaviors ? "Y" : "N")}");
}
await writer.WriteLineAsync();
}
private static async Task IsHandlerForSameExceptionWorks(IMediator mediator, WrappingWriter writer)
{
var isHandledCorrectly = false;
await writer.WriteLineAsync("Checking handler to catch exact exception...");
try
{
await mediator.Send(new PingProtectedResource { Message = "Ping to protected resource" });
isHandledCorrectly = IsExceptionHandledBy(writer);
}
catch (Exception e)
{
await writer.WriteLineAsync(e.Message);
}
await writer.WriteLineAsync();
return isHandledCorrectly;
}
private static async Task IsHandlerForBaseExceptionWorks(IMediator mediator, WrappingWriter writer)
{
var isHandledCorrectly = false;
await writer.WriteLineAsync("Checking shared handler to catch exception by base type...");
try
{
await mediator.Send(new PingResource { Message = "Ping to missed resource" });
isHandledCorrectly = IsExceptionHandledBy(writer);
}
catch (Exception e)
{
await writer.WriteLineAsync(e.Message);
}
await writer.WriteLineAsync();
return isHandledCorrectly;
}
private static async Task IsHandlerForLessSpecificExceptionWorks(IMediator mediator, WrappingWriter writer)
{
var isHandledCorrectly = false;
await writer.WriteLineAsync("Checking base handler to catch any exception...");
try
{
await mediator.Send(new PingResourceTimeout { Message = "Ping to ISS resource" });
isHandledCorrectly = IsExceptionHandledBy (writer);
}
catch (Exception e)
{
await writer.WriteLineAsync(e.Message);
}
await writer.WriteLineAsync();
return isHandledCorrectly;
}
private static async Task IsPreferredHandlerForBaseExceptionWorks(IMediator mediator, WrappingWriter writer)
{
var isHandledCorrectly = false;
await writer.WriteLineAsync("Selecting preferred handler to handle exception...");
try
{
await mediator.Send(new ExceptionHandler.Overrides.PingResourceTimeout { Message = "Ping to ISS resource (preferred)" });
isHandledCorrectly = IsExceptionHandledBy (writer);
}
catch (Exception e)
{
await writer.WriteLineAsync(e.Message);
}
await writer.WriteLineAsync();
return isHandledCorrectly;
}
private static async Task IsOverriddenHandlerForBaseExceptionWorks(IMediator mediator, WrappingWriter writer)
{
var isHandledCorrectly = false;
await writer.WriteLineAsync("Selecting new handler to handle exception...");
try
{
await mediator.Send(new PingNewResource { Message = "Ping to ISS resource (override)" });
isHandledCorrectly = IsExceptionHandledBy (writer);
}
catch (Exception e)
{
await writer.WriteLineAsync(e.Message);
}
await writer.WriteLineAsync();
return isHandledCorrectly;
}
private static bool IsExceptionHandledBy(WrappingWriter writer)
where TException : Exception
{
var messages = writer.Contents.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList();
if (messages.Count - 3 < 0)
return false;
// Note: For this handler type to be found in messages, it must be written in messages by LogExceptionAction
return messages[messages.Count - 2].Contains(typeof(THandler).FullName)
// Note: For this exception type to be found in messages, exception must be written in all tested exception handlers
&& messages[messages.Count - 3].Contains(typeof(TException).FullName);
}
}
public class RunResults
{
public bool RequestHandlers { get; set; }
public bool VoidRequestsHandlers { get; set; }
public bool PipelineBehaviors { get; set; }
public bool RequestPreProcessors { get; set; }
public bool RequestPostProcessors { get; set; }
public bool OrderedPipelineBehaviors { get; set; }
public bool ConstrainedGenericBehaviors { get; set; }
public bool NotificationHandler { get; set; }
public bool MultipleNotificationHandlers { get; set; }
public bool CovariantNotificationHandler { get; set; }
public bool ConstrainedGenericNotificationHandler { get; set; }
public bool HandlerForSameException { get; set; }
public bool HandlerForBaseException { get; set; }
public bool HandlerForLessSpecificException { get; set; }
public bool PreferredHandlerForBaseException { get; set; }
public bool OverriddenHandlerForBaseException { get; set; }
// Stream results
public bool StreamRequestHandlers { get; set; }
public bool StreamPipelineBehaviors { get; set; }
public bool StreamOrderedPipelineBehaviors { get; set; }
}
public class WrappingWriter : TextWriter
{
private readonly TextWriter _innerWriter;
private readonly StringBuilder _stringWriter = new StringBuilder();
public WrappingWriter(TextWriter innerWriter)
{
_innerWriter = innerWriter;
}
public override void Write(char value)
{
_stringWriter.Append(value);
_innerWriter.Write(value);
}
public override Task WriteLineAsync(string value)
{
_stringWriter.AppendLine(value);
return _innerWriter.WriteLineAsync(value);
}
public override Encoding Encoding => _innerWriter.Encoding;
public string Contents => _stringWriter.ToString();
}
================================================
FILE: samples/MediatR.Examples/Streams/GenericStreamPipelineBehavior.cs
================================================
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples;
public class GenericStreamPipelineBehavior : IStreamPipelineBehavior
{
private readonly TextWriter _writer;
public GenericStreamPipelineBehavior(TextWriter writer)
{
_writer = writer;
}
public async IAsyncEnumerable Handle(TRequest request, StreamHandlerDelegate next, [EnumeratorCancellation]CancellationToken cancellationToken)
{
await _writer.WriteLineAsync("-- Handling StreamRequest");
await foreach (var response in next().WithCancellation(cancellationToken).ConfigureAwait(false))
{
yield return response;
}
await _writer.WriteLineAsync("-- Finished StreamRequest");
}
}
================================================
FILE: samples/MediatR.Examples/Streams/Sing.cs
================================================
namespace MediatR.Examples;
public class Sing : IStreamRequest
{
public string Message { get; set; }
}
================================================
FILE: samples/MediatR.Examples/Streams/SingHandler.cs
================================================
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples;
public class SingHandler : IStreamRequestHandler
{
private readonly TextWriter _writer;
public SingHandler(TextWriter writer)
{
_writer = writer;
}
public async IAsyncEnumerable Handle(Sing request, [EnumeratorCancellation]CancellationToken cancellationToken)
{
await _writer.WriteLineAsync($"--- Handled Sing: {request.Message}, Song");
yield return await Task.Run(() => new Song { Message = request.Message + "ing do" });
yield return await Task.Run(() => new Song { Message = request.Message + "ing re" });
yield return await Task.Run(() => new Song { Message = request.Message + "ing mi" });
yield return await Task.Run(() => new Song { Message = request.Message + "ing fa" });
yield return await Task.Run(() => new Song { Message = request.Message + "ing so" });
yield return await Task.Run(() => new Song { Message = request.Message + "ing la" });
yield return await Task.Run(() => new Song { Message = request.Message + "ing ti" });
yield return await Task.Run(() => new Song { Message = request.Message + "ing do" });
}
}
================================================
FILE: samples/MediatR.Examples/Streams/Song.cs
================================================
namespace MediatR.Examples;
public class Song
{
public string Message { get; set; }
}
================================================
FILE: samples/MediatR.Examples.AspNetCore/MediatR.Examples.AspNetCore.csproj
================================================
net10.0
Exe
================================================
FILE: samples/MediatR.Examples.AspNetCore/Program.cs
================================================
using System;
using System.IO;
using System.Threading.Tasks;
using MediatR.Pipeline;
using Microsoft.Extensions.DependencyInjection;
namespace MediatR.Examples.AspNetCore;
public static class Program
{
public static Task Main(string[] args)
{
var writer = new WrappingWriter(Console.Out);
var mediator = BuildMediator(writer);
return Runner.Run(mediator, writer, "ASP.NET Core DI", testStreams: true);
}
private static IMediator BuildMediator(WrappingWriter writer)
{
var services = new ServiceCollection();
services.AddSingleton(writer);
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblies(typeof(Ping).Assembly, typeof(Sing).Assembly);
});
services.AddScoped(typeof(IStreamRequestHandler), typeof(SingHandler));
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(GenericPipelineBehavior<,>));
services.AddScoped(typeof(IRequestPreProcessor<>), typeof(GenericRequestPreProcessor<>));
services.AddScoped(typeof(IRequestPostProcessor<,>), typeof(GenericRequestPostProcessor<,>));
services.AddScoped(typeof(IStreamPipelineBehavior<,>), typeof(GenericStreamPipelineBehavior<,>));
var provider = services.BuildServiceProvider();
return provider.GetRequiredService();
}
}
================================================
FILE: samples/MediatR.Examples.Autofac/MediatR.Examples.Autofac.csproj
================================================
net10.0
Exe
================================================
FILE: samples/MediatR.Examples.Autofac/Program.cs
================================================
using Autofac.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
namespace MediatR.Examples.Autofac;
using global::Autofac;
using MediatR.Pipeline;
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
internal static class Program
{
public static Task Main(string[] args)
{
var writer = new WrappingWriter(Console.Out);
var mediator = BuildMediator(writer);
return Runner.Run(mediator, writer, "Autofac", testStreams: true);
}
private static IMediator BuildMediator(WrappingWriter writer)
{
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly).AsImplementedInterfaces();
var mediatrOpenTypes = new[]
{
typeof(IRequestHandler<,>),
typeof(IRequestExceptionHandler<,,>),
typeof(IRequestExceptionAction<,>),
typeof(INotificationHandler<>),
typeof(IStreamRequestHandler<,>)
};
foreach (var mediatrOpenType in mediatrOpenTypes)
{
builder
.RegisterAssemblyTypes(typeof(Ping).GetTypeInfo().Assembly)
.AsClosedTypesOf(mediatrOpenType)
// when having a single class implementing several handler types
// this call will cause a handler to be called twice
// in general you should try to avoid having a class implementing for instance `IRequestHandler<,>` and `INotificationHandler<>`
// the other option would be to remove this call
// see also https://github.com/LuckyPennySoftware/MediatR/issues/462
.AsImplementedInterfaces();
}
builder.RegisterInstance(writer).As();
// It appears Autofac returns the last registered types first
builder.RegisterGeneric(typeof(GenericStreamPipelineBehavior<,>)).As(typeof(IStreamPipelineBehavior<,>));
builder.RegisterGeneric(typeof(RequestPostProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(RequestPreProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(RequestExceptionActionProcessorBehavior<,>))
.As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(RequestExceptionProcessorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(GenericRequestPreProcessor<>)).As(typeof(IRequestPreProcessor<>));
builder.RegisterGeneric(typeof(GenericRequestPostProcessor<,>)).As(typeof(IRequestPostProcessor<,>));
builder.RegisterGeneric(typeof(GenericPipelineBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(ConstrainedRequestPostProcessor<,>)).As(typeof(IRequestPostProcessor<,>));
builder.RegisterGeneric(typeof(ConstrainedPingedHandler<>)).As(typeof(INotificationHandler<>));
var services = new ServiceCollection();
builder.Populate(services);
// The below returns:
// - RequestPreProcessorBehavior
// - RequestPostProcessorBehavior
// - GenericPipelineBehavior
// - GenericStreamPipelineBehavior
// - RequestExceptionActionProcessorBehavior
// - RequestExceptionProcessorBehavior
//var behaviors = container
// .Resolve>>()
// .ToList();
var container = builder.Build();
var serviceProvider = new AutofacServiceProvider(container);
var mediator = serviceProvider.GetRequiredService();
return mediator;
}
}
================================================
FILE: samples/MediatR.Examples.DryIoc/MediatR.Examples.DryIoc.csproj
================================================
net10.0
Exe
================================================
FILE: samples/MediatR.Examples.DryIoc/Program.cs
================================================
using System;
using System.IO;
using System.Threading.Tasks;
using DryIoc;
using DryIoc.Microsoft.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
namespace MediatR.Examples.DryIoc;
class Program
{
static Task Main()
{
var writer = new WrappingWriter(Console.Out);
var mediator = BuildMediator(writer);
return Runner.Run(mediator, writer, "DryIoc");
}
private static IMediator BuildMediator(WrappingWriter writer)
{
var container = new Container();
// Since Mediator has multiple constructors, consider adding rule to allow that
// var container = new Container(rules => rules.With(FactoryMethod.ConstructorWithResolvableArguments))
container.Use(writer);
//Pipeline works out of the box here
container.RegisterMany(new[] { typeof(IMediator).GetAssembly(), typeof(Ping).GetAssembly() }, Registrator.Interfaces);
//Without the container having FactoryMethod.ConstructorWithResolvableArguments commented above
//You must select the desired constructor
container.Register(made: Made.Of(() => new Mediator(Arg.Of())));
var services = new ServiceCollection();
var adapterContainer = container.WithDependencyInjectionAdapter(services);
return adapterContainer.GetRequiredService();
}
}
================================================
FILE: samples/MediatR.Examples.Lamar/MediatR.Examples.Lamar.csproj
================================================
Exe
net10.0
================================================
FILE: samples/MediatR.Examples.Lamar/Program.cs
================================================
using System;
using System.IO;
using System.Threading.Tasks;
using Lamar;
using MediatR.Pipeline;
namespace MediatR.Examples.Lamar;
class Program
{
static Task Main(string[] args)
{
var writer = new WrappingWriter(Console.Out);
var mediator = BuildMediator(writer);
return Runner.Run(mediator, writer, "Lamar");
}
private static IMediator BuildMediator(WrappingWriter writer)
{
var container = new Container(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssemblyContainingType();
scanner.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>));
scanner.ConnectImplementationsToTypesClosing(typeof(INotificationHandler<>));
scanner.ConnectImplementationsToTypesClosing(typeof(IRequestExceptionAction<,>));
scanner.ConnectImplementationsToTypesClosing(typeof(IRequestExceptionHandler<,,>));
});
//Pipeline
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionProcessorBehavior<,>));
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestExceptionActionProcessorBehavior<,>));
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestPreProcessorBehavior<,>));
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(RequestPostProcessorBehavior<,>));
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(GenericPipelineBehavior<,>));
cfg.For(typeof(IRequestPreProcessor<>)).Add(typeof(GenericRequestPreProcessor<>));
cfg.For(typeof(IRequestPostProcessor<,>)).Add(typeof(GenericRequestPostProcessor<,>));
cfg.For(typeof(IRequestPostProcessor<,>)).Add(typeof(ConstrainedRequestPostProcessor<,>));
//Constrained notification handlers
cfg.For(typeof(INotificationHandler<>)).Add(typeof(ConstrainedPingedHandler<>));
// This is the default but let's be explicit. At most we should be container scoped.
cfg.For().Use().Transient();
cfg.For().Use(writer);
});
var mediator = container.GetInstance();
return mediator;
}
}
================================================
FILE: samples/MediatR.Examples.LightInject/MediatR.Examples.LightInject.csproj
================================================
net10.0
Exe
================================================
FILE: samples/MediatR.Examples.LightInject/Program.cs
================================================
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using LightInject;
using LightInject.Microsoft.DependencyInjection;
using MediatR.Pipeline;
using Microsoft.Extensions.DependencyInjection;
namespace MediatR.Examples.LightInject;
class Program
{
static Task Main(string[] args)
{
var writer = new WrappingWriter(Console.Out);
var mediator = BuildMediator(writer);
return Runner.Run(mediator, writer, "LightInject");
}
private static IMediator BuildMediator(WrappingWriter writer)
{
var serviceContainer = new ServiceContainer(ContainerOptions.Default.WithMicrosoftSettings());
serviceContainer.Register();
serviceContainer.RegisterInstance(writer);
serviceContainer.RegisterAssembly(typeof(Ping).GetTypeInfo().Assembly, (serviceType, implementingType) =>
serviceType.IsConstructedGenericType &&
(
serviceType.GetGenericTypeDefinition() == typeof(IRequestHandler<,>) ||
serviceType.GetGenericTypeDefinition() == typeof(INotificationHandler<>)
));
serviceContainer.RegisterOrdered(typeof(IPipelineBehavior<,>),
new[]
{
typeof(RequestPreProcessorBehavior<,>),
typeof(RequestPostProcessorBehavior<,>),
typeof(GenericPipelineBehavior<,>)
}, type => null);
serviceContainer.RegisterOrdered(typeof(IRequestPostProcessor<,>),
new[]
{
typeof(GenericRequestPostProcessor<,>),
typeof(ConstrainedRequestPostProcessor<,>)
}, type => null);
serviceContainer.Register(typeof(IRequestPreProcessor<>), typeof(GenericRequestPreProcessor<>));
var services = new ServiceCollection();
var provider = serviceContainer.CreateServiceProvider(services);
return provider.GetRequiredService();
}
}
================================================
FILE: samples/MediatR.Examples.PublishStrategies/AsyncPingedHandler.cs
================================================
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples.PublishStrategies;
public class AsyncPingedHandler : INotificationHandler
{
public AsyncPingedHandler(string name)
{
Name = name;
}
public string Name { get; set; }
public async Task Handle(Pinged notification, CancellationToken cancellationToken)
{
if (Name == "2")
{
throw new ArgumentException("Name cannot be '2'");
}
Console.WriteLine($"[AsyncPingedHandler {Name}] {DateTime.Now:HH:mm:ss.fff} : Pinged");
await Task.Delay(100).ConfigureAwait(false);
Console.WriteLine($"[AsyncPingedHandler {Name}] {DateTime.Now:HH:mm:ss.fff} : After pinged");
}
}
================================================
FILE: samples/MediatR.Examples.PublishStrategies/CustomMediator.cs
================================================
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples.PublishStrategies;
public class CustomMediator : Mediator
{
private readonly Func, INotification, CancellationToken, Task> _publish;
public CustomMediator(IServiceProvider serviceFactory, Func, INotification, CancellationToken, Task> publish) : base(serviceFactory)
=> _publish = publish;
protected override Task PublishCore(IEnumerable handlerExecutors, INotification notification, CancellationToken cancellationToken)
=> _publish(handlerExecutors, notification, cancellationToken);
}
================================================
FILE: samples/MediatR.Examples.PublishStrategies/MediatR.Examples.PublishStrategies.csproj
================================================
Exe
net10.0
================================================
FILE: samples/MediatR.Examples.PublishStrategies/Program.cs
================================================
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace MediatR.Examples.PublishStrategies;
class Program
{
static async Task Main(string[] args)
{
var services = new ServiceCollection();
services.AddSingleton();
services.AddTransient>(sp => new SyncPingedHandler("1"));
services.AddTransient>(sp => new AsyncPingedHandler("2"));
services.AddTransient>(sp => new AsyncPingedHandler("3"));
services.AddTransient>(sp => new SyncPingedHandler("4"));
var provider = services.BuildServiceProvider();
var publisher = provider.GetRequiredService();
var pinged = new Pinged();
foreach (PublishStrategy strategy in Enum.GetValues(typeof(PublishStrategy)))
{
Console.WriteLine($"Strategy: {strategy}");
Console.WriteLine("----------");
try
{
await publisher.Publish(pinged, strategy);
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType()}: {ex.Message}");
}
await Task.Delay(1000);
Console.WriteLine("----------");
}
Console.WriteLine("done");
}
}
================================================
FILE: samples/MediatR.Examples.PublishStrategies/PublishStrategy.cs
================================================
namespace MediatR.Examples.PublishStrategies;
///
/// Strategy to use when publishing notifications
///
public enum PublishStrategy
{
///
/// Run each notification handler after one another. Returns when all handlers are finished. In case of any exception(s), they will be captured in an AggregateException.
///
SyncContinueOnException = 0,
///
/// Run each notification handler after one another. Returns when all handlers are finished or an exception has been thrown. In case of an exception, any handlers after that will not be run.
///
SyncStopOnException = 1,
///
/// Run all notification handlers asynchronously. Returns when all handlers are finished. In case of any exception(s), they will be captured in an AggregateException.
///
Async = 2,
///
/// Run each notification handler on its own thread using Task.Run(). Returns immediately and does not wait for any handlers to finish. Note that you cannot capture any exceptions, even if you await the call to Publish.
///
ParallelNoWait = 3,
///
/// Run each notification handler on its own thread using Task.Run(). Returns when all threads (handlers) are finished. In case of any exception(s), they are captured in an AggregateException by Task.WhenAll.
///
ParallelWhenAll = 4,
///
/// Run each notification handler on its own thread using Task.Run(). Returns when any thread (handler) is finished. Note that you cannot capture any exceptions (See msdn documentation of Task.WhenAny)
///
ParallelWhenAny = 5,
}
================================================
FILE: samples/MediatR.Examples.PublishStrategies/Publisher.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples.PublishStrategies;
public class Publisher
{
private readonly IServiceProvider _serviceFactory;
public Publisher(IServiceProvider serviceFactory)
{
_serviceFactory = serviceFactory;
PublishStrategies[PublishStrategy.Async] = new CustomMediator(_serviceFactory, AsyncContinueOnException);
PublishStrategies[PublishStrategy.ParallelNoWait] = new CustomMediator(_serviceFactory, ParallelNoWait);
PublishStrategies[PublishStrategy.ParallelWhenAll] = new CustomMediator(_serviceFactory, ParallelWhenAll);
PublishStrategies[PublishStrategy.ParallelWhenAny] = new CustomMediator(_serviceFactory, ParallelWhenAny);
PublishStrategies[PublishStrategy.SyncContinueOnException] = new CustomMediator(_serviceFactory, SyncContinueOnException);
PublishStrategies[PublishStrategy.SyncStopOnException] = new CustomMediator(_serviceFactory, SyncStopOnException);
}
public IDictionary PublishStrategies = new Dictionary();
public PublishStrategy DefaultStrategy { get; set; } = PublishStrategy.SyncContinueOnException;
public Task Publish(TNotification notification)
{
return Publish(notification, DefaultStrategy, default(CancellationToken));
}
public Task Publish(TNotification notification, PublishStrategy strategy)
{
return Publish(notification, strategy, default(CancellationToken));
}
public Task Publish(TNotification notification, CancellationToken cancellationToken)
{
return Publish(notification, DefaultStrategy, cancellationToken);
}
public Task Publish(TNotification notification, PublishStrategy strategy, CancellationToken cancellationToken)
{
if (!PublishStrategies.TryGetValue(strategy, out var mediator))
{
throw new ArgumentException($"Unknown strategy: {strategy}");
}
return mediator.Publish(notification, cancellationToken);
}
private Task ParallelWhenAll(IEnumerable handlers, INotification notification, CancellationToken cancellationToken)
{
var tasks = new List();
foreach (var handler in handlers)
{
tasks.Add(Task.Run(() => handler.HandlerCallback(notification, cancellationToken)));
}
return Task.WhenAll(tasks);
}
private Task ParallelWhenAny(IEnumerable handlers, INotification notification, CancellationToken cancellationToken)
{
var tasks = new List();
foreach (var handler in handlers)
{
tasks.Add(Task.Run(() => handler.HandlerCallback(notification, cancellationToken)));
}
return Task.WhenAny(tasks);
}
private Task ParallelNoWait(IEnumerable handlers, INotification notification, CancellationToken cancellationToken)
{
foreach (var handler in handlers)
{
Task.Run(() => handler.HandlerCallback(notification, cancellationToken));
}
return Task.CompletedTask;
}
private async Task AsyncContinueOnException(IEnumerable handlers, INotification notification, CancellationToken cancellationToken)
{
var tasks = new List();
var exceptions = new List();
foreach (var handler in handlers)
{
try
{
tasks.Add(handler.HandlerCallback(notification, cancellationToken));
}
catch (Exception ex) when (!(ex is OutOfMemoryException || ex is StackOverflowException))
{
exceptions.Add(ex);
}
}
try
{
await Task.WhenAll(tasks).ConfigureAwait(false);
}
catch (AggregateException ex)
{
exceptions.AddRange(ex.Flatten().InnerExceptions);
}
catch (Exception ex) when (!(ex is OutOfMemoryException || ex is StackOverflowException))
{
exceptions.Add(ex);
}
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
}
private async Task SyncStopOnException(IEnumerable handlers, INotification notification, CancellationToken cancellationToken)
{
foreach (var handler in handlers)
{
await handler.HandlerCallback(notification, cancellationToken).ConfigureAwait(false);
}
}
private async Task SyncContinueOnException(IEnumerable handlers, INotification notification, CancellationToken cancellationToken)
{
var exceptions = new List();
foreach (var handler in handlers)
{
try
{
await handler.HandlerCallback(notification, cancellationToken).ConfigureAwait(false);
}
catch (AggregateException ex)
{
exceptions.AddRange(ex.Flatten().InnerExceptions);
}
catch (Exception ex) when (!(ex is OutOfMemoryException || ex is StackOverflowException))
{
exceptions.Add(ex);
}
}
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
}
}
================================================
FILE: samples/MediatR.Examples.PublishStrategies/SyncPingedHandler.cs
================================================
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR.Examples.PublishStrategies;
public class SyncPingedHandler : INotificationHandler
{
public SyncPingedHandler(string name)
{
Name = name;
}
public string Name { get; set; }
public Task Handle(Pinged notification, CancellationToken cancellationToken)
{
if (Name == "2")
{
throw new ArgumentException("Name cannot be '2'");
}
Console.WriteLine($"[SyncPingedHandler {Name}] {DateTime.Now:HH:mm:ss.fff} : Pinged");
Thread.Sleep(100);
Console.WriteLine($"[SyncPingedHandler {Name}] {DateTime.Now:HH:mm:ss.fff} : After pinged");
return Task.CompletedTask;
}
}
================================================
FILE: samples/MediatR.Examples.SimpleInjector/MediatR.Examples.SimpleInjector.csproj
================================================
net10.0
Exe
================================================
FILE: samples/MediatR.Examples.SimpleInjector/Program.cs
================================================
using System.IO;
using System.Threading.Tasks;
using MediatR.Pipeline;
using Microsoft.Extensions.DependencyInjection;
namespace MediatR.Examples.SimpleInjector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using global::SimpleInjector;
internal static class Program
{
private static Task Main(string[] args)
{
var writer = new WrappingWriter(Console.Out);
var mediator = BuildMediator(writer);
return Runner.Run(mediator, writer, "SimpleInjector", true);
}
private static IMediator BuildMediator(WrappingWriter writer)
{
var container = new Container();
var services = new ServiceCollection();
services
.AddSimpleInjector(container);
var assemblies = GetAssemblies().ToArray();
container.RegisterSingleton();
container.Register(typeof(IRequestHandler<,>), assemblies);
RegisterHandlers(container, typeof(INotificationHandler<>), assemblies);
RegisterHandlers(container, typeof(IRequestExceptionAction<,>), assemblies);
RegisterHandlers(container, typeof(IRequestExceptionHandler<,,>), assemblies);
RegisterHandlers(container, typeof(IStreamRequestHandler<,>), assemblies);
container.Register(() => (TextWriter) writer, Lifestyle.Singleton);
//Pipeline
container.Collection.Register(typeof(IPipelineBehavior<,>), new[]
{
typeof(RequestExceptionProcessorBehavior<,>),
typeof(RequestExceptionActionProcessorBehavior<,>),
typeof(RequestPreProcessorBehavior<,>),
typeof(RequestPostProcessorBehavior<,>),
typeof(GenericPipelineBehavior<,>)
});
container.Collection.Register(typeof(IRequestPreProcessor<>), new[] { typeof(GenericRequestPreProcessor<>) });
container.Collection.Register(typeof(IRequestPostProcessor<,>), new[] { typeof(GenericRequestPostProcessor<,>), typeof(ConstrainedRequestPostProcessor<,>) });
container.Collection.Register(typeof(IStreamPipelineBehavior<,>), new[]
{
typeof(GenericStreamPipelineBehavior<,>)
});
var serviceProvider = services.BuildServiceProvider().UseSimpleInjector(container);
container.RegisterInstance(container);
var mediator = container.GetRequiredService();
return mediator;
}
private static void RegisterHandlers(Container container, Type collectionType, Assembly[] assemblies)
{
// we have to do this because by default, generic type definitions (such as the Constrained Notification Handler) won't be registered
var handlerTypes = container.GetTypesToRegister(collectionType, assemblies, new TypesToRegisterOptions
{
IncludeGenericTypeDefinitions = true,
IncludeComposites = false,
});
container.Collection.Register(collectionType, handlerTypes);
}
private static IEnumerable GetAssemblies()
{
yield return typeof(IMediator).GetTypeInfo().Assembly;
yield return typeof(Ping).GetTypeInfo().Assembly;
}
}
================================================
FILE: samples/MediatR.Examples.Stashbox/MediatR.Examples.Stashbox.csproj
================================================
Exe
net10.0
================================================
FILE: samples/MediatR.Examples.Stashbox/Program.cs
================================================
using Stashbox;
using Stashbox.Configuration;
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace MediatR.Examples.Stashbox;
class Program
{
static Task Main()
{
var writer = new WrappingWriter(Console.Out);
var mediator = BuildMediator(writer);
return Runner.Run(mediator, writer, "Stashbox", testStreams: true);
}
private static IMediator BuildMediator(WrappingWriter writer)
{
var container = new StashboxContainer()
.RegisterInstance(writer)
.RegisterAssemblies(new[] { typeof(Mediator).Assembly, typeof(Ping).Assembly },
serviceTypeSelector: Rules.ServiceRegistrationFilters.Interfaces, registerSelf: false);
return container.GetRequiredService();
}
}
================================================
FILE: samples/MediatR.Examples.Windsor/ContravariantFilter.cs
================================================
namespace MediatR.Examples.Windsor;
using System;
using System.Linq;
using System.Reflection;
using Castle.MicroKernel;
public class ContravariantFilter : IHandlersFilter
{
public bool HasOpinionAbout(Type service)
{
if (!service.IsGenericType)
return false;
var genericType = service.GetGenericTypeDefinition();
var genericArguments = genericType.GetGenericArguments();
return genericArguments.Count() == 1
&& genericArguments.Single().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant);
}
public IHandler[] SelectHandlers(Type service, IHandler[] handlers)
{
return handlers;
}
}
================================================
FILE: samples/MediatR.Examples.Windsor/MediatR.Examples.Windsor.csproj
================================================
net6.0
Exe
================================================
FILE: samples/MediatR.Examples.Windsor/Program.cs
================================================
using System.Linq;
using System.Threading.Tasks;
using Castle.MicroKernel.Resolvers.SpecializedResolvers;
using MediatR.Pipeline;
namespace MediatR.Examples.Windsor;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
internal class Program
{
private static Task Main(string[] args)
{
var writer = new WrappingWriter(Console.Out);
var mediator = BuildMediator(writer);
return Runner.Run(mediator, writer, "Castle.Windsor", true);
}
private static IMediator BuildMediator(WrappingWriter writer)
{
var container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
container.Kernel.AddHandlersFilter(new ContravariantFilter());
// *** The default lifestyle for Windsor is Singleton
// *** If you are using ASP.net, it's better to register your services with 'Per Web Request LifeStyle'.
var fromAssemblyContainingPing = Classes.FromAssemblyContaining();
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestHandler<,>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(fromAssemblyContainingPing.BasedOn(typeof(INotificationHandler<>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestExceptionProcessorBehavior<,>)));
container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestExceptionActionProcessorBehavior<,>)));
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestExceptionAction<,>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestExceptionHandler<,,>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IStreamRequestHandler<,>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestPreProcessor<>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestPostProcessor<,>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(Component.For().ImplementedBy());
container.Register(Component.For().Instance(writer));
//container.Register(Component.For().UsingFactoryMethod(k => (type =>
//{
// var enumerableType = type
// .GetInterfaces()
// .Concat(new[] { type })
// .FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));
// var service = enumerableType?.GetGenericArguments()?[0];
// var resolvedType = enumerableType != null ? k.ResolveAll(service) : k.Resolve(type);
// var genericArguments = service?.GetGenericArguments();
// // Handle exceptions even using the base request types for IRequestExceptionHandler<,,>
// var isRequestExceptionHandler = service?.GetGenericTypeDefinition()
// ?.IsAssignableTo(typeof(IRequestExceptionHandler<,,>)) ?? false;
// if (isRequestExceptionHandler)
// return ResolveRequestExceptionHandler(k, type, service, resolvedType, genericArguments);
// // Handle exceptions even using the base request types for IRequestExceptionAction<,>
// var isRequestExceptionAction = service?.GetGenericTypeDefinition()
// ?.IsAssignableTo(typeof(IRequestExceptionAction<,>)) ?? false;
// if (isRequestExceptionAction)
// return ResolveRequestExceptionAction(k, type, service, resolvedType, genericArguments);
// return resolvedType;
//})));
//Pipeline
container.Register(Component.For(typeof(IStreamPipelineBehavior<,>)).ImplementedBy(typeof(GenericStreamPipelineBehavior<,>)));
container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestPreProcessorBehavior<,>)).NamedAutomatically("PreProcessorBehavior"));
container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestPostProcessorBehavior<,>)).NamedAutomatically("PostProcessorBehavior"));
container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(GenericPipelineBehavior<,>)).NamedAutomatically("Pipeline"));
container.Register(Component.For(typeof(IRequestPreProcessor<>)).ImplementedBy(typeof(GenericRequestPreProcessor<>)).NamedAutomatically("PreProcessor"));
container.Register(Component.For(typeof(IRequestPostProcessor<,>)).ImplementedBy(typeof(GenericRequestPostProcessor<,>)).NamedAutomatically("PostProcessor"));
container.Register(Component.For(typeof(IRequestPostProcessor<,>), typeof(ConstrainedRequestPostProcessor<,>)).NamedAutomatically("ConstrainedRequestPostProcessor"));
container.Register(Component.For(typeof(INotificationHandler<>), typeof(ConstrainedPingedHandler<>)).NamedAutomatically("ConstrainedPingedHandler"));
var mediator = container.Resolve();
return mediator;
}
private static object ResolveRequestExceptionHandler(IKernel k, Type type, Type service, object resolvedType, Type[] genericArguments)
{
if (service == null
|| genericArguments == null
|| !service.IsInterface
|| !service.IsGenericType
|| !service.IsConstructedGenericType
|| !(service.GetGenericTypeDefinition()
?.IsAssignableTo(typeof(IRequestExceptionHandler<,,>)) ?? false)
|| genericArguments.Length != 3)
{
return resolvedType;
}
var serviceFactory = k.Resolve();
var baseRequestType = genericArguments[0].BaseType;
var response = genericArguments[1];
var exceptionType = genericArguments[2];
// Check if the base request type is valid
if (baseRequestType == null
|| !baseRequestType.IsClass
|| baseRequestType == typeof(object)
|| ((!baseRequestType.GetInterfaces()
?.Any(i => i.IsAssignableFrom(typeof(IRequest<>)))) ?? true))
{
return resolvedType;
}
var exceptionHandlerInterfaceType = typeof(IRequestExceptionHandler<,,>).MakeGenericType(baseRequestType, response, exceptionType);
var enumerableExceptionHandlerInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionHandlerInterfaceType);
Array resultArray = CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(type, resolvedType, serviceFactory, enumerableExceptionHandlerInterfaceType);
return resultArray;
}
private static object ResolveRequestExceptionAction(IKernel k, Type type, Type service, object resolvedType, Type[] genericArguments)
{
if (service == null
|| genericArguments == null
|| !service.IsInterface
|| !service.IsGenericType
|| !service.IsConstructedGenericType
|| !(service.GetGenericTypeDefinition()
?.IsAssignableTo(typeof(IRequestExceptionAction<,>)) ?? false)
|| genericArguments.Length != 2)
{
return resolvedType;
}
var serviceFactory = k.Resolve();
var baseRequestType = genericArguments[0].BaseType;
var exceptionType = genericArguments[1];
// Check if the base request type is valid
if (baseRequestType == null
|| !baseRequestType.IsClass
|| baseRequestType == typeof(object)
|| ((!baseRequestType.GetInterfaces()
?.Any(i => i.IsAssignableFrom(typeof(IRequest<>)))) ?? true))
{
return resolvedType;
}
var exceptionHandlerInterfaceType = typeof(IRequestExceptionAction<,>).MakeGenericType(baseRequestType, exceptionType);
var enumerableExceptionHandlerInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionHandlerInterfaceType);
Array resultArray = CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(type, resolvedType, serviceFactory, enumerableExceptionHandlerInterfaceType);
return resultArray;
}
private static Array CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(Type type, object resolvedType, ServiceFactory serviceFactory, Type enumerableExceptionHandlerInterfaceType)
{
var firstArray = serviceFactory.Invoke(enumerableExceptionHandlerInterfaceType) as Array;
Debug.Assert(firstArray != null, $"Array '{nameof(firstArray)}' should not be null because this method calls ResolveAll when a {typeof(IEnumerable<>).FullName} " +
$"is passed as argument in argument named '{nameof(type)}'");
var secondArray = resolvedType is Array ? resolvedType as Array : new[] { resolvedType };
Debug.Assert(secondArray != null, $"Array '{nameof(secondArray)}' should not be null because '{nameof(resolvedType)}' is an array or created as an array");
var resultArray = Array.CreateInstance(typeof(object), firstArray.Length + secondArray.Length);
Array.Copy(firstArray, resultArray, firstArray.Length);
Array.Copy(secondArray, 0, resultArray, firstArray.Length, secondArray.Length);
return resultArray;
}
}
================================================
FILE: src/MediatR/Entities/OpenBehavior.cs
================================================
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
namespace MediatR.Entities;
///
/// Represents a registration entity for pipeline behaviors with a specified service lifetime.
///
public class OpenBehavior
{
///
/// Initializes a new instance of the class.
///
/// The type of the pipeline behavior to register.
/// The lifetime of the registered service. Defaults to Transient.
/// Thrown if the specified type does not implement IPipelineBehavior.
/// Thrown if is null.
public OpenBehavior(Type openBehaviorType, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
{
ValidatePipelineBehaviorType(openBehaviorType);
OpenBehaviorType = openBehaviorType;
ServiceLifetime = serviceLifetime;
}
///
/// The type of the open behavior.
///
public Type OpenBehaviorType { get; }
///
/// The service lifetime of the open behavior.
///
public ServiceLifetime ServiceLifetime { get; }
///
/// Validates whether the specified type implements the interface.
///
/// The type to validate.
/// Thrown if the type does not implement .
/// Thrown if is null.
private static void ValidatePipelineBehaviorType(Type openBehaviorType)
{
if (openBehaviorType == null) throw new ArgumentNullException($"Open behavior type can not be null.");
bool isPipelineBehavior = openBehaviorType.GetInterfaces()
.Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPipelineBehavior<,>));
if (!isPipelineBehavior)
{
throw new InvalidOperationException($"The type \"{openBehaviorType.Name}\" must implement IPipelineBehavior<,> interface.");
}
}
}
================================================
FILE: src/MediatR/IMediator.cs
================================================
namespace MediatR;
///
/// Defines a mediator to encapsulate request/response and publishing interaction patterns
///
public interface IMediator : ISender, IPublisher
{
}
================================================
FILE: src/MediatR/INotificationHandler.cs
================================================
using System.Threading;
using System.Threading.Tasks;
namespace MediatR;
///
/// Defines a handler for a notification
///
/// The type of notification being handled
public interface INotificationHandler
where TNotification : INotification
{
///
/// Handles a notification
///
/// The notification
/// Cancellation token
Task Handle(TNotification notification, CancellationToken cancellationToken);
}
///
/// Wrapper class for a synchronous notification handler
///
/// The notification type
public abstract class NotificationHandler : INotificationHandler
where TNotification : INotification
{
Task INotificationHandler.Handle(TNotification notification, CancellationToken cancellationToken)
{
Handle(notification);
return Task.CompletedTask;
}
///
/// Override in a derived class for the handler logic
///
/// Notification
protected abstract void Handle(TNotification notification);
}
================================================
FILE: src/MediatR/INotificationPublisher.cs
================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
namespace MediatR;
public interface INotificationPublisher
{
Task Publish(IEnumerable handlerExecutors, INotification notification,
CancellationToken cancellationToken);
}
================================================
FILE: src/MediatR/IPipelineBehavior.cs
================================================
namespace MediatR;
using System.Threading;
using System.Threading.Tasks;
///
/// Represents an async continuation for the next task to execute in the pipeline
///
/// Response type
/// Awaitable task returning a
public delegate Task RequestHandlerDelegate(CancellationToken t = default);
///
/// Pipeline behavior to surround the inner handler.
/// Implementations add additional behavior and await the next delegate.
///
/// Request type
/// Response type
public interface IPipelineBehavior where TRequest : notnull
{
///
/// Pipeline handler. Perform any additional behavior and await the delegate as necessary
///
/// Incoming request
/// Awaitable delegate for the next action in the pipeline. Eventually this delegate represents the handler.
/// Cancellation token
/// Awaitable task returning the
Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancellationToken);
}
================================================
FILE: src/MediatR/IPublisher.cs
================================================
using System.Threading;
using System.Threading.Tasks;
namespace MediatR;
///
/// Publish a notification or event through the mediator pipeline to be handled by multiple handlers.
///
public interface IPublisher
{
///
/// Asynchronously send a notification to multiple handlers
///
/// Notification object
/// Optional cancellation token
/// A task that represents the publish operation.
Task Publish(object notification, CancellationToken cancellationToken = default);
///
/// Asynchronously send a notification to multiple handlers
///
/// Notification object
/// Optional cancellation token
/// A task that represents the publish operation.
Task Publish(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : INotification;
}
================================================
FILE: src/MediatR/IRequestHandler.cs
================================================
using System.Threading;
using System.Threading.Tasks;
namespace MediatR;
///
/// Defines a handler for a request
///
/// The type of request being handled
/// The type of response from the handler
public interface IRequestHandler
where TRequest : IRequest
{
///
/// Handles a request
///
/// The request
/// Cancellation token
/// Response from the request
Task Handle(TRequest request, CancellationToken cancellationToken);
}
///
/// Defines a handler for a request with a void response.
///
/// The type of request being handled
public interface IRequestHandler
where TRequest : IRequest
{
///
/// Handles a request
///
/// The request
/// Cancellation token
/// Response from the request
Task Handle(TRequest request, CancellationToken cancellationToken);
}
================================================
FILE: src/MediatR/ISender.cs
================================================
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediatR;
///
/// Send a request through the mediator pipeline to be handled by a single handler.
///
public interface ISender
{
///
/// Asynchronously send a request to a single handler
///
/// Response type
/// Request object
/// Optional cancellation token
/// A task that represents the send operation. The task result contains the handler response
Task Send(IRequest request, CancellationToken cancellationToken = default);
///
/// Asynchronously send a request to a single handler with no response
///
/// Request object
/// Optional cancellation token
/// A task that represents the send operation.
Task Send(TRequest request, CancellationToken cancellationToken = default)
where TRequest : IRequest;
///
/// Asynchronously send an object request to a single handler via dynamic dispatch
///
/// Request object
/// Optional cancellation token
/// A task that represents the send operation. The task result contains the type erased handler response
Task