Skip to content

ali-golshani/Mediator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mediator with Custom Pipelines

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

Usage

Define a Marker Interface or Base Class for Each Pipeline requests

public interface IRequestA { }

Define Requests and Request Handlers

To implement the Mediator class without relying on 'C# Reflection', define the request type as a generic argument within the IRequest interface.

public interface IRequest<in TRequest, out TResponse> { }

public sealed class RequestX : IRequest<RequestX, string>, IRequestA
{
    // Implementation details
}

public sealed class RequestXHandler : IRequestHandler<RequestX, string>
{
    public Task<string> Handle(RequestX request, CancellationToken cancellationToken)
    {
        // ...
    }
}

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
}

Define Pipelines

Pipelines can be defined in two ways:

a. Using the Pipeline Base Class

The Pipeline base class accepts a request handler (IRequestHandler) along with pipeline middlewares (IMiddleware) as input parameters.

public abstract class Pipeline<TRequest, TResponse> : IPipeline<TRequest, TResponse>
    where TRequest : IRequest<TRequest, TResponse>
{
    protected Pipeline(
        IRequestHandler<TRequest, TResponse> handler,
        params IMiddleware<TRequest, TResponse>[] middlewares)
    {
        // Base pipeline implementation
    }
}

public abstract class PipelineA<TRequest, TResponse> : Pipeline<TRequest, TResponse>
    where TRequest : IRequest<TRequest, TResponse>, IRequestA
{
    public PipelineA(
        IRequestHandler<TRequest, TResponse> handler,
        ExceptionHandlingMiddleware<TRequest, TResponse> exceptionHandling,
        ValidationMiddleware<TRequest, TResponse> validation)
        : base(handler, exceptionHandling, validation)
    { }
}

Register the pipeline in Dependency Injection (DI):

services.AddScoped(typeof(IPipeline<,>), typeof(PipelineA<,>));

b. Using the KeyedPipeline Base Class (Recommended)

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

public abstract class KeyedPipeline<TRequest, TResponse> : IPipeline<TRequest, TResponse>
    where TRequest : IRequest<TRequest, TResponse>
{
    protected KeyedPipeline(IServiceProvider serviceProvider, string pipelineName)
    { 
         // ...
    }

    // Keyed pipeline base class implementation
}

internal sealed class PipelineB<TRequest, TResponse> : KeyedPipeline<TRequest, TResponse>
    where TRequest : IRequest<TRequest, TResponse>, IRequestB
{
    public PipelineB(IServiceProvider serviceProvider)
        : base(serviceProvider, PipelineBConfiguration.PipelineName)
    { }
}

internal sealed class PipelineBConfiguration : IKeyedPipelineConfiguration
{
    public static string PipelineName { get; } = "Pipeline_B";

    public static Type[] Middlewares()
    {
        return
        [
            typeof(ExceptionHandlingMiddleware<,>),
            typeof(ValidationMiddleware<,>),
            typeof(SpecialMiddleware<,>),
        ];
    }
}

Register the pipeline in DI:

services.AddTransient(typeof(IPipeline<,>), typeof(PipelineB<,>));
services.RegisterMiddlewares<PipelineBConfiguration>();

Use IMediator to handle requests

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

About

Simple Mediator

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages