MediatR - 1.1

Extension ID

com.castsoftware.dotnet.mediatr

What’s new?

See MediatR for .NET - 1.1 - Release Notes for more information.

Description

This extension provides support for libraries such as MediatR, Mediator.Abstractions, FluentMediator, which implement the Mediator design pattern on .NET

In what situation should you install this extension?

If your .NET application uses Mediator design pattern for messaging and you want to view a model that shows the transaction between the message publisher and handler object with their corresponding links

Technology support

The following libraries are supported by this extension:

Language Library name Namespace Version Supported
.NET MediatRexternal link MediatR 0.1.0 to 12.3.0
.NET Mediator.Abstractionsexternal link Mediator 1.0.5 to 2.1.7
.NET FluentMediatorexternal link FluentMediator 0.0.1 to 0.4.7
.NET Mediator.Netexternal link Mediator.Net 1.0.0 to 4.8.0
.NET Elsa.Mediatorexternal link Elsa.Mediator 3.0.0 to 3.2.3
.NET Handyman.Mediatorexternal link Handyman.Mediator 1.0.0 to 12.0.0

Compatibility

Core release Operating System Supported
8.4.x Microsoft Windows / Linux
8.3.x Microsoft Windows

Dependencies

The MediatR extension is dependent on following extensions:

  • CAST AIP Internal extension (com.castsoftware.internal.platform)

Download and installation instructions

The extension will not be automatically downloaded and installed in CAST Console. If you need to use it, you should manually install the extension using the Application - Extensions interface:

What results can you expect?

Once the analysis/snapshot generation has completed, you can view the below objects and links created.

Objects

Icon Description Comment
MediatR DotNet Publisher an object is created for each message published and message type is resolved
MediatR DotNet Handler an object is created for each resolved Message Handler
MediatR Unknown DotNet Publisher an object is created for each message published and message type is not resolved in a method call
Link Type Source and Destination Link Supported APIs
callLink Link between the caller C# method and the DotNet MediatR Publisher object
MediatR Publisher APIsMediatR.IMediator.Publish
MediatR.IMediator.PublishAsync
MediatR.IMediator.Send
MediatR.IMediator.SendAsync
MediatR.IMediator.Stream
MediatR.IMediator.CreateStream
MediatR.ISender.Send
MediatR.ISender.CreateStream
MediatR.IPublisher.Publish
MediatR.Mediator.Publish
MediatR.Mediator.Send
Mediator Abstractions Publisher APIsMediator.IPublisher.Publish
Mediator.ISender.Send
Mediator.ISender.CreateStream
FluentMediator Publisher APIsFluentMediator.Pipelines.PipelineAsync.IAsyncMediator.PublishAsync
FluentMediator.Pipelines.CancellablePipelineAsync.ICancellablePipelineAsync.PublishAsync
FluentMediator.Pipelines.PipelineAsync.IPipelineAsync.PublishAsync
FluentMediator.Pipelines.CancellablePipelineAsync.ICancellableMediator.PublishAsync
FluentMediator.Pipelines.Pipeline.ISyncMediator.Publish
FluentMediator.Pipelines.PipelineAsync.IPipelineAsync.SendAsync
FluentMediator.Pipelines.Pipeline.IPipeline.Publish
FluentMediator.Pipelines.PipelineAsync.IAsyncMediator.SendAsync
FluentMediator.Pipelines.CancellablePipelineAsync.ICancellablePipelineAsync.SendAsync
FluentMediator.Pipelines.CancellablePipelineAsync.ICancellableMediator.SendAsync
FluentMediator.Pipelines.Pipeline.ISyncMediator.Send
FluentMediator.Pipelines.Pipeline.IPipeline.Send
Mediator.Net Publisher APIsMediator.Net.IMediator.SendAsync
Mediator.Net.IMediator.PublishAsync
Mediator.Net.IMediator.RequestAsync
Mediator.Net.IMediator.CreateStream
Elsa Mediator Publisher APIsElsa.Mediator.Contracts.ICommandSender.SendAsync
Elsa.Mediator.Contracts.INotificationSender.SendAsync
Elsa.Mediator.Contracts.IRequestSender.SendAsync
Handyman.Mediator Publisher APIsHandyman.Mediator.IPublisher.Publish
Handyman.Mediator.ISender.Send
Handyman.Mediator.IMediator.Send
Handyman.Mediator.IMediator.Publish
Handyman.Mediator.IDynamicMediator.Publish
Handyman.Mediator.IDynamicMediator.Send
Handyman.Mediator.IDynamicPublisher.Publish
Handyman.Mediator.IDynamicSender.Send
callLink Link between the DotNet MediatR Handler object and the caller C# method
MediatR Handler APIsMediatR.IRequestHandler
MediatR.INotificationHandler
MediatR.IStreamRequestHandler
MediatR.IAsyncNotificationHandler
MediatR.IAsyncRequestHandler
MediatR.IAsyncRequestStreamHandler
Mediator Abstractions Handler APIsMediator.ICommandHandler
Mediator.INotificationHandler
Mediator.IQueryHandler
Mediator.IRequestHandler
Mediator.IStreamCommandHandler
Mediator.IStreamQueryHandler
Mediator.IStreamRequestHandler
FluentMediator Handler APIsFluentMediator.Pipelines.PipelineAsync.IPipelineAsyncBuilder.Call
FluentMediator.Pipelines.PipelineAsync.IPipelineAsyncBuilder.Return
FluentMediator.Pipelines.CancellablePipelineAsync.ICancellablePipelineAsyncBuilder.Call
FluentMediator.Pipelines.CancellablePipelineAsync.ICancellablePipelineAsyncBuilder.Return
FluentMediator.Pipelines.Pipeline.IPipelineBuilder.Call
FluentMediator.Pipelines.Pipeline.IPipelineBuilder.Return
Mediator.Net Handler APIsMediator.Net.Contracts.ICommandHandler
Mediator.Net.Contracts.IStreamRequestHandler
Mediator.Net.Contracts.IEventHandler
Mediator.Net.Contracts.IRequestHandler
Mediator.Net.Contracts.IStreamCommandHandler
Elsa Mediator Handler APIsElsa.Mediator.Contracts.ICommandHandler
Elsa.Mediator.Contracts.INotificationHandler
Elsa.Mediator.Contracts.IRequestHandler
Handyman.Mediator Handler APIsHandyman.Mediator.IEventHandler
Handyman.Mediator.IRequestHandler

Code Examples

Publisher

MediatR Publish

using MediatR;
using Microsoft.Extensions.DependencyInjection;

public class Benchmarks
{
    private IMediator _mediator;
    private readonly Pinged _notification = new Pinged();

    public void GlobalSetup()
    {
        var services = new ServiceCollection();
        services.AddMediatR(cfg =>
        {
            cfg.RegisterServicesFromAssemblyContaining(typeof(Ping));
        });
        var provider = services.BuildServiceProvider();
        _mediator = provider.GetRequiredService<IMediator>();
    }

    public Task PublishingNotifications()
    {
        return _mediator.Publish(_notification);
    }
}

MediatR Send


private static async Task<bool> IsHandlerForLessSpecificExceptionWorks(IMediator mediator, WrappingWriter writer)
    {
        var isHandledCorrectly = false;

        await writer.WriteLineAsync("Checking base handler to catch any exception...");
        try
        {
            await mediator.Send(new PingResourceTimeout { Message = "Ping to ISS resource" });
            isHandledCorrectly = IsExceptionHandledBy<TaskCanceledException, CommonExceptionHandler> (writer);
        }
        catch (Exception e)
        {
            await writer.WriteLineAsync(e.Message);
        }
        await writer.WriteLineAsync();

        return isHandledCorrectly;
    }

MediatR CreateStream

public async Task Should_register_and_wrap_with_behavior()
    {
        var output = new Logger();
        IServiceCollection services = new ServiceCollection();
        services.AddSingleton(output);
        services.AddMediatR(cfg =>
        {
            cfg.RegisterServicesFromAssembly(typeof(Ping).Assembly);
            cfg.
        });
        var provider = services.BuildServiceProvider();

        var mediator = provider.GetRequiredService<IMediator>();

        var stream = mediator.CreateStream(new StreamPing { Message = "Ping" });

        await foreach (var response in stream)
        {
            response.Message.ShouldBe("Ping Pang");
        }
    }

Unknown Publisher

MediatR Unknown DotNet Publihser object is created where the message type that is being sent cannot be resolved

public Task Should_raise_execption_on_null_request() => Should.ThrowAsync<ArgumentNullException>(async () => await _mediator.Send(default!));

FluentMediator

using FluentMediator;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class TaskService : ITaskService
{
    private readonly ITaskRepository _taskRepository;
    private readonly ITaskFactory _taskFactory;
    private readonly TaskViewModelMapper _taskViewModelMapper;
    private readonly ITracer _tracer;
    private readonly IMediator _mediator;

    public TaskService(ITaskRepository taskRepository, TaskViewModelMapper taskViewModelMapper, ITracer tracer, ITaskFactory taskFactory, IMediator mediator)
    {
        _taskRepository = taskRepository;
        _taskViewModelMapper = taskViewModelMapper;
        _tracer = tracer;
        _taskFactory = taskFactory;
        _mediator = mediator;
    }

    public async System.Threading.Tasks.Task Delete(Guid id)
    {
        using (var scope = _tracer.BuildSpan("Delete_TaskService").StartActive(true))
        {
            var deleteTaskCommand = _taskViewModelMapper.ConvertToDeleteTaskCommand(id);
            await _mediator.PublishAsync(deleteTaskCommand);
        }
    }
}

Mediator.Net

using System;
using System.Threading.Tasks;
using Mediator.Net
using Microsoft.AspNetCore.Mvc;

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly IMediator _mediator;

    public ValuesController(IMediator mediator)
    {
        _mediator = mediator;
    }

    [HttpPost]
    [Route(("event"))]
    public async Task PostEvent(int id, [FromBody]EventData value)
    {
        await _mediator.PublishAsync(new ResultCalculatedEvent(value.Result));
    }
    
}

Handyman.Mediator

using Handyman.Mediator;
using Microsoft.Extensions.DependencyInjection;

public class PublisherSample : Sample
{
   public override async Task RunAsync(CancellationToken cancellationToken)
   {
      var mediator = ServiceProvider.GetRequiredService<IPublisher<Event>>();

      var @event = new Event();

      await mediator.Publish(@event, cancellationToken);
   }
}

Handlers

MediatR NotificationHandler

using MediatR;

public class PingedHandler : INotificationHandler<Pinged>
{
    public Task Handle(Pinged notification, CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

MediatR RequestHandler

using MediatR;

public class PingResourceTimeoutHandler : IRequestHandler<PingResourceTimeout, Pong>
{
    private readonly TextWriter _writer;

    public PingResourceTimeoutHandler(TextWriter writer) => _writer = writer;

    public Task<Pong> Handle(PingResourceTimeout request, CancellationToken cancellationToken)
    {
        throw new TaskCanceledException();
    }
}

MediatR StreamRequestHandler

using MediatR;

public class PingStreamHandler : IStreamRequestHandler<StreamPing, Pong>
    {
        private readonly Logger _output;

        public PingStreamHandler(Logger output)
        {
            _output = output;
        }
        public async IAsyncEnumerable<Pong> Handle(StreamPing request, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            _output.Messages.Add("Handler");
            yield return await Task.Run(() => new Pong { Message = request.Message + " Pang" }, cancellationToken);
        }
    }

FluentMediator Handler

using System;
using System.Collections.Generic;

using FluentMediator;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;


public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();

        services.AddScoped<TaskCommandHandler>();
        services.AddScoped<TaskEventHandler>();

        services.AddFluentMediator(builder =>
        {
            builder.On<CreateNewTaskCommand>().PipelineAsync().Return<Domain.Tasks.Task, TaskCommandHandler>((handler, request) => handler.HandleNewTask(request));

            builder.On<TaskCreatedEvent>().PipelineAsync().Call<TaskEventHandler>((handler, request) => handler.HandleTaskCreatedEvent(request));

            builder.On<DeleteTaskCommand>().PipelineAsync().Call<TaskCommandHandler>((handler, request) => handler.HandleDeleteTask(request));

            builder.On<TaskDeletedEvent>().PipelineAsync().Call<TaskEventHandler>((handler, request) => handler.HandleTaskDeletedEvent(request));
        });

    }
    }

Mediator.Net.Contracts Handler

using Mediator.Net.Context;
using Mediator.Net.Contracts;
using Mediator.Net.WebApiSample.Handlers.CommandHandler;

namespace Mediator.Net.WebApiSample.Handlers.EventHandler
{
    public class ResultCalculatedEventHandler: IEventHandler<ResultCalculatedEvent>
    {
        private readonly IBoardcastService _boardcastService;

        public ResultCalculatedEventHandler(IBoardcastService boardcastService)
        {
            _boardcastService = boardcastService;
        }
        public Task Handle(IReceiveContext<ResultCalculatedEvent> context, CancellationToken cancellationToken)
        {
            _boardcastService.Boardcast(context.Message.Result);
            return Task.FromResult(0);
        }
    }
}

Handyman.Mediator Handler

using Handyman.Mediator;
using Microsoft.Extensions.DependencyInjection;

public class Handler : IEventHandler<Event>
{
   public async Task Handle(Event @event, CancellationToken cancellationToken)
   {
      await Task.Yield();

      Console.WriteLine(GetType().Name);
   }
}