Code Monkey home page Code Monkey logo

mediator.net's Introduction

Build status Mediator.Net on Stack Overflow

Mediator.Net

A mediator project for .NET

Get Packages

You can get Mediator.Net by grabbing the latest NuGet packages.

Get Started

Install the nuget package Mediator.Net

	Install-Package Mediator.Net

Setup the mediator by using MediatorBuilder

  
	var mediaBuilder = new MediatorBuilder();
	var mediator = mediaBuilder.RegisterHandlers(typeof(this).Assembly).Build();
           

###Handlers scan registration

	var mediaBuilder = new MediatorBuilder();
	var mediator = mediaBuilder.RegisterHandlers(typeof(this).Assembly).Build();
           

###Handlers explicit registration

  
	var mediator = builder.RegisterHandlers(() =>
        {
            var binding = new List<MessageBinding>
            {
                new MessageBinding(typeof(TestBaseCommand), typeof(TestBaseCommandHandler)),
                new MessageBinding(typeof(DerivedTestBaseCommand), typeof(DerivedTestBaseCommandHandler))
            };
            return binding;
        }).Build();
           

###Sending a command, publishing event and sending request and getting response

	await _mediator.SendAsync(new TestBaseCommand(Guid.NewGuid()));
	await _mediator.PublishAsync(new TestEvent(Guid.NewGuid()));
	var result = await _mediator.RequestAsync<GetGuidRequest, GetGuidResponse>(new GetGuidRequest(_guid));

###Handling message from handler Once a message is sent, it will reach its handlers, you can only have one handler for ICommand and IRequest and can have multi handlers for IEvent. ReceiveContext will be delivered to the handler.

	class TestBaseCommandHandler : ICommandHandler<TestBaseCommand>
    {
        
        public Task Handle(ReceiveContext<TestBaseCommand> context)
        {
            Console.WriteLine(context.Message.Id);
            RubishBox.Rublish.Add(nameof(TestBaseCommandHandler));
            return Task.FromResult(0);
        }
    }
	
	// Or in async 
	class AsyncTestBaseCommandHandler : ICommandHandler<TestBaseCommand>
    {
        public async Task Handle(ReceiveContext<TestBaseCommand> context)
        {
            RubishBox.Rublish.Add(nameof(AsyncTestBaseCommandHandler));
            Console.WriteLine(context.Message.Id);
            await Task.FromResult(0);
        }
    }

###Using pipelines There are 5 different type of pipelines you can use image

####GlobalReceivePipeline This pipeline will be triggered whenever a message is sent, published or requested before it reach the next pipeline and handler

####CommandReceivePipeline This pipeline will be triggered just after the GlobalReceivePipeline and before it reach its command handler, this pipeline will only used for ICommand

####EventReceivePipeline This pipeline will be triggered just after the GlobalReceivePipeline and before it reach its event handler/handlers, this pipeline will only used for IEvent

####RequestReceivePipeline This pipeline will be triggered just after the GlobalReceivePipeline and before it reach its request handler, this pipeline will only used for IRequest

####PublishPipeline This pipeline will be triggered when an IEvent is published inside your handler, this pipeline will only used for IEvent, it is usually being used as outgoing interceptor

###Setting up middlewares The most powerful thing for the pipelines above is you can add as many middlewares as you want. Follow the following steps to setup a middlewaree

  • Add a static class for your middleware
  • Add a public static extension method in that class you just added, usually follow the UseXxxx naming convention
  • Add another class for your middleware's specification, note that this is the implementation of your middleware

Note that, in order to make the framework both both with IoC and without, you can either pass your instance in or resolve the instance by the provided DependancyScope from the IPipeConfigurator

An example is shown below

Middleware class

	 public static class SerilogMiddleware
    {
        public static void UseSerilog<TContext>(this IPipeConfigurator<TContext> configurator, LogEventLevel logAsLevel, ILogger logger = null)
            where TContext : IContext<IMessage>
        {
            if (logger == null && configurator.DependancyScope == null)
            {
                throw new DependancyScopeNotConfiguredException($"{nameof(ILogger)} is not provided and IDependancyScope is not configured, Please ensure {nameof(ILogger)} is registered properly if you are using IoC container, otherwise please pass {nameof(ILogger)} as parameter");
            }
            logger = logger ?? configurator.DependancyScope.Resolve<ILogger>();
            
            configurator.AddPipeSpecification(new SerilogMiddlewareSpecification<TContext>(logger, logAsLevel));
        }
    }

Specification class

    class SerilogMiddlewareSpecification<TContext> : IPipeSpecification<TContext>
        where TContext : IContext<IMessage>
    {
        private readonly ILogger _logger;
        private readonly LogEventLevel _level;

        public SerilogMiddlewareSpecification(ILogger logger, LogEventLevel level)
        {
            _logger = logger;
            _level = level;
        }
        public bool ShouldExecute(TContext context)
        {
            return true;

        }

        public Task ExecuteBeforeConnect(TContext context)
        {
            if (ShouldExecute(context))
            {
                switch (_level)
                {
                    case LogEventLevel.Error:
                        _logger.Error("Receive message {@Message}", context.Message);
                            break;
                    case LogEventLevel.Debug:
                        _logger.Debug("Receive message {@Message}", context.Message);
                        break;
                    case LogEventLevel.Fatal:
                        _logger.Fatal("Receive message {@Message}", context.Message);
                        break;
                    case LogEventLevel.Information:
                        _logger.Information("Receive message {@Message}", context.Message); 
                        break;
                    case LogEventLevel.Verbose:
                        _logger.Verbose("Receive message {@Message}", context.Message);
                        break;
                    case LogEventLevel.Warning:
                        _logger.Verbose("Receive message {@Message}", context.Message);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
            return Task.FromResult(0);
        }

        public Task ExecuteAfterConnect(TContext context)
        {
            return Task.FromResult(0);
        }
    }

To hook up middlewares into pipelines

	
    var builder = new MediatorBuilder();
     _mediator = builder.RegisterHandlers(() =>
         {
             var binding = new List<MessageBinding>()
             {
                 new MessageBinding(typeof(TestBaseCommand), typeof(TestBaseCommandHandlerRaiseEvent)),
                 new MessageBinding(typeof(TestEvent), typeof(TestEventHandler)),
                 new MessageBinding(typeof(GetGuidRequest), typeof(GetGuidRequestHandler))
             };
             return binding;
         })
         .ConfigureGlobalReceivePipe(x =>
         {
             x.UseDummySave();
         })
         .ConfigureCommandReceivePipe(x =>
         {
             x.UseConsoleLogger1();
         })
         .ConfigureEventReceivePipe(x =>
         {
             x.UseConsoleLogger2();
         })
         .ConfigureRequestPipe(x =>
         {
             x.UseConsoleLogger3();
         })
         .ConfigurePublishPipe(x =>
         {
             x.UseConsoleLogger4();
         })
     .Build();   

###ReceiveContext in Handlers As you might already noticed, mediator will deliver ReceiveContext to the handler and it has a property Message which is the original message sent, in some cases you might have one event being handled in multiple handlers and you might want to share something between, ReceiveContext would be a very good place that to register your service or instance. For example you can make a middleware and register the service from there. ####Register DummyTransaction from middleware

	public class SimpleMiddlewareSpecification<TContext> : IPipeSpecification<TContext>
        where TContext : IContext<IMessage>
    {
        public bool ShouldExecute(TContext context)
        {
            return true;

        }

        public Task ExecuteBeforeConnect(TContext context)
        {
            if (ShouldExecute(context))
            {
                Console.WriteLine($"Before 1: {context.Message}");
                context.RegisterService(new DummyTransaction());
            }

            return Task.FromResult(0);

        }

        public Task ExecuteAfterConnect(TContext context)
        {
            if (ShouldExecute(context))
                Console.WriteLine($"After 1: {context.Message}");
            return Task.FromResult(0);
        }
    }

####Get the service from the handler

	public Task Handle(ReceiveContext<SimpleCommand> context)
    {
        _simpleService.DoWork();
        DummyTransaction transaction;
        if (context.TryGetService(out transaction))
        {
            transaction.Commit();
        }
        return Task.FromResult(0);
    }

###Using dependancy injection(IoC) frameworks ####Autofac Install the nuget package Mediator.Net.Autofac

	Install-Package Mediator.Net.Autofac

An extension method RegisterMediator for ContainerBuilder from Autofac is used to register the builder

The super simple use case

	
	var mediaBuilder = new MediatorBuilder();
    mediaBuilder.RegisterHandlers(typeof(TestContainer).Assembly);
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterMediator(mediaBuilder);
    _container = containerBuilder.Build();  

You can also setup middlewares for each pipe before register it

	var mediaBuilder = new MediatorBuilder();
    mediaBuilder.RegisterHandlers(typeof(TestContainer).Assembly)
        .ConfigureCommandReceivePipe(x =>
        {
            x.UseSimpleMiddleware();
        });
    var containerBuilder = new ContainerBuilder();
    containerBuilder.RegisterMediator(mediaBuilder);
    _container = containerBuilder.Build();  

####StructureMap

	Install-Package Mediator.Net.StructureMap

Setup an IContainer and do your normal registration, then pass it along with the MediatorBuilder to the StructureMapExtensions class to register Mediator.Net

var mediaBuilder = new MediatorBuilder();
    mediaBuilder.RegisterHandlers(TestUtilAssembly.Assembly)
        .ConfigureCommandReceivePipe(x =>
        {
            x.UseSimpleMiddleware();
        });
    _container = new Container();
    _container.Configure(x =>
    {
        // Do your thing
    });
    StructureMapExtensions.Configure(mediaBuilder, _container);

####Unity

	Install-Package Mediator.Net.Unity

Setup an IUnityContainer and do your normal registration, then pass it along with the MediatorBuilder to the UnityExtensions class to register Mediator.Net

var mediaBuilder = new MediatorBuilder();
    var mediaBuilder = new MediatorBuilder();
    mediaBuilder.RegisterHandlers(TestUtilAssembly.Assembly)
        .ConfigureCommandReceivePipe(x =>
        {
            x.UseSimpleMiddleware();
        });
    _container = new UnityContainer();
    _container.RegisterType<SimpleService>();
    _container.RegisterType<AnotherSimpleService>();

    UnityExtensioins.Configure(mediaBuilder, _container);

####SimpleInjector

	Install-Package Mediator.Net.SimpleInjector

We have created a helper class InjectHelper to register all necessary components for Mediator.Net

	var mediaBuilder = new MediatorBuilder();
    mediaBuilder.RegisterHandlers(TestUtilAssembly.Assembly)
        .ConfigureCommandReceivePipe(x =>
        {
            x.UseSimpleMiddleware();
        });
    _container = new Container();
    _container.Options.DefaultScopedLifestyle = new LifetimeScopeLifestyle();
    _container.Register<SimpleService>();
    _container.Register<AnotherSimpleService>();
    
    InjectHelper.RegisterMediator(_container, mediaBuilder);

Thought that you can have transient registration for IMediator, but we recommend to use lifetime scope, you can do constructor injection as well as the following

	using (var scope = _container.BeginLifetimeScope())
    {
        _mediator = scope.GetInstance<IMediator>();
        _task = _mediator.RequestAsync<SimpleRequest, SimpleResponse>(new SimpleRequest());
    }

Middlewares

One of the key feature for Mediator.Net is you can plug as many middlewares as you like, we have implemented some common one as below ###Mediator.Net.Middlewares.UnitOfWork

	Install-Package Mediator.Net.Middlewares.UnitOfWork

This middleware provide a CommittableTransaction inside the context, handlers can enlist the transaction if it requires UnitOfWork Mediator.Net.Middlewares.UnitOfWork - Middleware for Mediator.Net to support unit of work.

###Mediator.Net.Middlewares.Serilog

	Install-Package Mediator.Net.Middlewares.Serilog

This middleware logs every message by using Serilog

###Mediator.Net.Middlewares.EventStore

	Install-Package Mediator.Net.Middlewares.EventStore

Middleware for Mediator.Net to write event to GetEventStore, it is a Middleware for Mediator.Net that plugs intothe publish pipeline Mediator.Net.Middlewares.UnitOfWork - Middleware for Mediator.Net to persist event to EventStore.

mediator.net's People

Contributors

mayuanyang avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.