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