Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A simple mediator for .Net for sending command, publishing event and request response with pipelines supported

License

NotificationsYou must be signed in to change notification settings

mayuanyang/Mediator.Net

Repository files navigation

Mediator.Net on Stack Overflow

Build statusexample workflowexample workflowcodecov

Mediator.Net

A mediator project for .NET

logo_sm

Get Packages

You can get Mediator.Net bygrabbing the latest NuGet packages.

Get Started

Install the nuget package Mediator.Net

Install-Package Mediator.Net

Simple usage

// Setup a mediator buildervarmediaBuilder=newMediatorBuilder();varmediator=mediaBuilder.RegisterHandlers(typeof(this).Assembly).Build();

Sending a command with no response

await_mediator.SendAsync(newTestBaseCommand(Guid.NewGuid()));

Sending a command with response

varpong=await_mediator.SendAsync<Ping,Pong>(newPing());

Sending request with response

varresult=await_mediator.RequestAsync<GetGuidRequest,GetGuidResponse>(newGetGuidRequest(_guid));

Publishing an event

await_mediator.Publish(newOrderPlacedEvent);

Publishing an event as the result of a command

Inside a command handler.Handle method, a IReceiveContext expose a method of Publish

publicasyncTaskHandle(IReceiveContext<DerivedTestBaseCommand>context,CancellationTokencancellationToken){// Do you workawaitcontext.Publish(newOrderPlacedEvent());}

Create stream of responses

Sometimes you might want to get multiple responses by one request or command, you can do that by using theCreateStream method

// Define a StreamHandler by implementing the IStreamRequestHandler or IStreamCommandHandler interfaces for IRequest and ICommandpublicclassGetMultipleGuidStreamRequestHandler:IStreamRequestHandler<GetGuidRequest,GetGuidResponse>{publicasyncIAsyncEnumerable<GetGuidResponse>Handle(IReceiveContext<GetGuidRequest>context,[EnumeratorCancellation]CancellationTokencancellationToken){for(vari=0;i<5;i++){awaitTask.Delay(100,cancellationToken);yieldreturnawaitTask.FromResult(newGetGuidResponse(Guid.NewGuid()){Index=i});}}}// You can now get multiple responses back by using thisIAsyncEnumerable<GetGuiResponse>result=mediator.CreateStream<GetGuidRequest,GetGuidResponse>(newGetGuidRequest(_guid));awaitforeach(varrinresult){Console.WriteLine(r.Id.ToString());}

How about EventHandler?What would be the use cases of a stream of events? So it is currently not supported

How about middleware?You can use middleware as normal, keep in mind that middleware will only get invoked once for each IRequest or ICommand thought that multiple responses might return

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.

classTestBaseCommandHandler:ICommandHandler<TestBaseCommand>{publicTaskHandle(ReceiveContext<TestBaseCommand>context){Console.WriteLine(context.Message.Id);returnTask.FromResult(0);}}// Or in asyncclassAsyncTestBaseCommandHandler:ICommandHandler<TestBaseCommand>{publicasyncTaskHandle(ReceiveContext<TestBaseCommand>context){Console.WriteLine(context.Message.Id);awaitTask.FromResult(0);}}

Handler Registration

Handlers explicit registration

varmediator=builder.RegisterHandlers(()=>{varbinding=newList<MessageBinding>{newMessageBinding(typeof(TestBaseCommand),typeof(TestBaseCommandHandler)),newMessageBinding(typeof(DerivedTestBaseCommand),typeof(DerivedTestBaseCommandHandler))};returnbinding;}).Build();

Scan registration

varmediaBuilder=newMediatorBuilder();varmediator=mediaBuilder.RegisterHandlers(typeof(this).Assembly).Build();

Using pipelines

There are 5 different type of pipelines you can useimage

GlobalReceivePipeline

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

CommandReceivePipeline

This pipeline will be triggered just after theGlobalReceivePipeline and before it reaches its command handler, this pipeline will only be used forICommand

EventReceivePipeline

This pipeline will be triggered just after theGlobalReceivePipeline and before it reaches its event handler/handlers, this pipeline will only be used forIEvent

RequestReceivePipeline

This pipeline will be triggered just after theGlobalReceivePipeline and before it reaches its request handler, this pipeline will only be used forIRequest

PublishPipeline

This pipeline will be triggered when anIEvent is published inside your handler, this pipeline will only be used forIEvent and 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 middleware

  • 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

You might need some dependencies in your middleware, there are two ways to do it

  • Pass them in explicitly
  • Let the IoC container to resolve it for you (if you are using IoC)

Here is a sample middleware

Middleware class

publicstaticclassSerilogMiddleware{publicstaticvoidUseSerilog<TContext>(thisIPipeConfigurator<TContext>configurator,LogEventLevellogAsLevel,ILoggerlogger=null)whereTContext:IContext<IMessage>{if(logger==null&&configurator.DependencyScope==null){thrownewDependencyScopeNotConfiguredException($"{nameof(ILogger)} is not provided and IDependencyScope 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.DependencyScope.Resolve<ILogger>();configurator.AddPipeSpecification(newSerilogMiddlewareSpecification<TContext>(logger,logAsLevel));}}

Specification class

classSerilogMiddlewareSpecification<TContext>:IPipeSpecification<TContext>whereTContext:IContext<IMessage>{privatereadonlyILogger_logger;privatereadonlyFunc<bool>_shouldExecute;privatereadonlyLogEventLevel_level;publicSerilogMiddlewareSpecification(ILoggerlogger,LogEventLevellevel,Func<bool>shouldExecute){_logger=logger;_level=level;_shouldExecute=shouldExecute;}publicboolShouldExecute(TContextcontext,CancellationTokencancellationToken){if(_shouldExecute==null){returntrue;}return_shouldExecute.Invoke();}publicTaskBeforeExecute(TContextcontext,CancellationTokencancellationToken){returnTask.FromResult(0);}publicTaskExecute(TContextcontext,CancellationTokencancellationToken){if(ShouldExecute(context,cancellationToken)){switch(_level){caseLogEventLevel.Error:_logger.Error("Receive message {@Message}",context.Message);break;caseLogEventLevel.Debug:_logger.Debug("Receive message {@Message}",context.Message);break;caseLogEventLevel.Fatal:_logger.Fatal("Receive message {@Message}",context.Message);break;caseLogEventLevel.Information:_logger.Information("Receive message {@Message}",context.Message);break;caseLogEventLevel.Verbose:_logger.Verbose("Receive message {@Message}",context.Message);break;caseLogEventLevel.Warning:_logger.Verbose("Receive message {@Message}",context.Message);break;default:thrownewArgumentOutOfRangeException();}}returnTask.FromResult(0);}publicTaskAfterExecute(TContextcontext,CancellationTokencancellationToken){returnTask.FromResult(0);}publicvoidOnException(Exceptionex,TContextcontext){throwex;}}

To hook up middlewares into pipelines

varbuilder=newMediatorBuilder();_mediator=builder.RegisterHandlers(()=>{returnnewList<MessageBinding>(){newMessageBinding(typeof(TestBaseCommand),typeof(TestBaseCommandHandlerRaiseEvent)),newMessageBinding(typeof(TestEvent),typeof(TestEventHandler)),newMessageBinding(typeof(GetGuidRequest),typeof(GetGuidRequestHandler))};}).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 propertyMessage 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 is 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

publicclassSimpleMiddlewareSpecification<TContext>:IPipeSpecification<TContext>whereTContext:IContext<IMessage>{publicboolShouldExecute(TContextcontext){returntrue;}publicTaskBeforeExecute(TContextcontext){returnTask.FromResult(0);}publicTaskExecute(TContextcontext){if(ShouldExecute(context)){context.RegisterService(newDummyTransaction());}returnTask.FromResult(0);}publicTaskAfterExecute(TContextcontext){returnTask.FromResult(0);}}

Get the DummyTransaction registered in the middleware from the handler

publicTaskHandle(ReceiveContext<SimpleCommand>context){_simpleService.DoWork();if(context.TryGetService(outDummyTransactiontransaction)){transaction.Commit();}returnTask.FromResult(0);}

Using dependency 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

varmediaBuilder=newMediatorBuilder();mediaBuilder.RegisterHandlers(typeof(TestContainer).Assembly);varcontainerBuilder=newContainerBuilder();containerBuilder.RegisterMediator(mediaBuilder);_container=containerBuilder.Build();

You can also setup middlewares for each pipe before register it

varmediaBuilder=newMediatorBuilder();mediaBuilder.RegisterHandlers(typeof(TestContainer).Assembly).ConfigureCommandReceivePipe(x=>{x.UseSimpleMiddleware();});varcontainerBuilder=newContainerBuilder();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

varmediaBuilder=newMediatorBuilder();mediaBuilder.RegisterHandlers(TestUtilAssembly.Assembly).ConfigureCommandReceivePipe(x=>{x.UseSimpleMiddleware();});_container=newContainer();_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

varmediaBuilder=newMediatorBuilder();varmediaBuilder=newMediatorBuilder();mediaBuilder.RegisterHandlers(TestUtilAssembly.Assembly).ConfigureCommandReceivePipe(x=>{x.UseSimpleMiddleware();});_container=newUnityContainer();_container.RegisterType<SimpleService>();_container.RegisterType<AnotherSimpleService>();UnityExtensions.Configure(mediaBuilder,_container);

SimpleInjector

Install-Package Mediator.Net.SimpleInjector

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

varmediaBuilder=newMediatorBuilder();mediaBuilder.RegisterHandlers(TestUtilAssembly.Assembly).ConfigureCommandReceivePipe(x=>{x.UseSimpleMiddleware();});_container=newContainer();_container.Options.DefaultScopedLifestyle=newLifetimeScopeLifestyle();_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(varscope=_container.BeginLifetimeScope()){_mediator=scope.GetInstance<IMediator>();_task=_mediator.RequestAsync<SimpleRequest,SimpleResponse>(newSimpleRequest());}

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 UnitOfWorkMediator.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 events to GetEventStore, it is a Middleware for Mediator.Net that plugs into the publish pipelineMediator.Net.Middlewares.UnitOfWork - Middleware for Mediator.Net to persist event to EventStore.

About

A simple mediator for .Net for sending command, publishing event and request response with pipelines supported

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

    Packages

    No packages published

    [8]ページ先頭

    ©2009-2025 Movatter.jp