Skip to content

ali-golshani/Mediator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

68 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Minimal Mediator with Custom Pipelines

This mediator enables the definition of different pipelines for various request types, allowing for greater flexibility and control.

NuGet Package

dotnet add package Minimal.Mediator

Usage

Define a marker interface or base class for each pipeline requests

public interface IRequestA { }

Define requests and request handlers

public sealed class Ping : IRequest<Ping, Pong>, IRequestA
{
    // Implementation details
}

public sealed class PingHandler : IRequestHandler<Ping, Pong>
{
    public Task<Pong> Handle(Ping request, CancellationToken cancellationToken)
    {
        // ...
    }
}

The TRequest generic parameter in the IRequest interface is designed to eliminate the need for C# Reflection in the implementation of the Mediator class. This approach ensures type safety and significantly reduces the runtime overhead commonly associated with reflection-based solutions.

Define middlewares

Custom middleware can be created to process general or specific request types. C#'s generic type constraints (where keyword) can be used to enforce type restrictions.

public sealed class ExceptionHandlingMiddleware<TRequest, TResponse> : IMiddleware<TRequest, TResponse>
{
    public async Task<TResponse> Handle(RequestContext<TRequest> context, IRequestProcessor<TRequest, TResponse> next)
    {
        try
        {
            return await next.Handle(context);
        }
        catch (Exception exp)
        {
            // Exception handling logic
        }
    }
}

public sealed class SpecialMiddleware<TRequest, TResponse> : IMiddleware<TRequest, TResponse>
    where TRequest : ISpecialRequest
{
    // Middleware implementation
}

public sealed class PingMiddleware : IMiddleware<Ping, Pong>
{
    // Middleware implementation
}

Define pipelines

Define Pipelines by using the KeyedPipeline Base Class.

This approach involves defining a uniquely named pipeline class along with a configuration class that implements the IKeyedPipelineConfiguration interface.

internal static class PipelineA
{
    public sealed class Pipeline<TRequest, TResponse> : KeyedPipeline<TRequest, TResponse>
        where TRequest : IRequest<TRequest, TResponse>, IRequestA
    {
        public Pipeline(IServiceProvider serviceProvider)
            : base(serviceProvider, Configuration.PipelineName)
        { }
    }

    public sealed class Configuration : IKeyedPipelineConfiguration
    {
        public static string PipelineName { get; } = "Pipeline_A";

        public static MiddlewareDescriptor[] Middlewares()
        {
            return
            [
                new MiddlewareDescriptor(typeof(ExceptionHandlingMiddleware<,>)),
                new MiddlewareDescriptor(typeof(ValidationMiddleware<,>)),
                new MiddlewareDescriptor(typeof(SpecialMiddleware<,>)),
                new MiddlewareDescriptor(typeof(PingMiddleware), typeof(IMiddleware<Ping, Pong>)),
            ];
        }
    }
}

Register in DI

/// Register Mediator
services.AddMediator();

///Register Pipeline
services.AddKeyedPipeline<PipelineA.Configuration>(typeof(PipelineA.Pipeline<,>));

To automatically register Request-Handlers and Notification-Handlers, consider using the ServiceScan.SourceGenerator package or the Scrutor package if Native AOT is not a concern (code).

If a request of type TRequest does not have a registered pipeline, it will be forwarded directly to its designated handler.

Use IMediator to handle requests

private static async Task Sample(IMediator mediator, CancellationToken cancellationToken)
{
    var request = new Ping() { /* ... */ };
    var response = await mediator.Send(request, cancellationToken);
    // ...
}

You can also utilize the ISender<,> service directly for request handling, ensuring a streamlined and efficient process.

private static async Task Sample(ISender<Ping, Pong> sender, CancellationToken cancellationToken)
{
    var request = new Ping() { /* ... */ };
    var response = await sender.Send(request, cancellationToken);
    // ...
}

Please refer to the sample project for more details.

About

Simple Mediator with Custom Pipelines

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages