Posted on
Request -> Handler -> SubPub Pattern with MediatR or Raw F# code
We will try:
- MediatR
- Mailbox
- Event
- Rx
- Agent
MediatR
Example:
openMediatR// Define the request type inheriting from `IRequest`. Here it's `MyRequest`.typeMyRequest()=inheritIRequest// Define the handler that takes the request as a parameter.lethandler(req:MyRequest)=printfn"Handling request"// Get the singleton instance of `Mediator`.letmediator=Mediator.Instance// Create the request.letreq=MyRequest()// Use `mediator.Send` to send the request and pipe the return value to the `handler`.mediator.Send(req)|>handler`
Main steps:
- Define a request type inheriting from IRequest. Here it's MyRequest.
- Define a handler function handler that takes a request as a parameter.
- Obtain a singleton instance of Mediator.
- Create a request req.
- Use mediator.Send to send the request and pass the return value through the handler.
- This allows you to use MediatR's pipeline to send commands or requests and handle them. You can continue to expand by adding more request types, handlers, etc.
Next, design a subscription:
openMediatR// Define the event type inheriting from `INotification`. Here it's `MyEvent`.typeMyEvent()=inheritINotification// Define the event handler function.lethandler(evt:MyEvent)=printfn"Handling event"letmediator=Mediator.Instance// Subscribe to the event.mediator.Subscribe(typeof<MyEvent>,handler)// Publish the event.mediator.Publish(MyEvent())
Main steps:
- Define an event type inheriting from INotification. Here it's MyEvent.
- Define an event handler function handler that takes an event as a parameter.
- Get the singleton instance of Mediator.
- Use mediator.Subscribe to subscribe to the event, specifying the event type and handler.
- Use mediator.Publish to publish the event.
This allows you to implement a publish-subscribe pattern. You can continue to expand by adding more event types, handlers, etc.
Combining them: Using F# functional programming style, combine the request handler and event subscription into a MediatR example:
openMediatR// Define the request.typeSayHelloRequest(name:string)=inheritRequestmemberthis.Name=name// Define the handler.letsayHelloHandler(request:SayHelloRequest)=printfn"Hello %s"request.Name// Define the event.typeUserGreetedEvent(name:string)=inheritNotificationmemberthis.Name=name// Define the event handler.letuserGreetedHandler(evt:UserGreetedEvent)=printfn"User %s was greeted"evt.Name// Request handling process.letsendHellonamemediator=letrequest=SayHelloRequest(name)mediator.Send(request)|>sayHelloHandler// Event subscription.letsubscribemediator=mediator.Subscribe(typeof<UserGreetedEvent>,userGreetedHandler)// Orchestrator function combining calls.letorchestratormediatorname=subscribemediatorsendHellonamemediatormediator.Publish(UserGreetedEvent(name))// Get the Mediator and run.letmediator=Mediator.Instanceorchestratormediator"John"
Running result:
Hello John User John was greeted
By calling MediatR's Send method to send a request, Subscribe method to subscribe to an event, and Publish method to publish an event, you can implement both request-response and publish-subscribe patterns.
F# raw code
Can F# bare code implement the request and subscription pattern?
Certainly. You can use MailboxProcessor, but the encapsulation and ease of use are not as good as MediatR.
Here's an example of implementing subscriptions with MailboxProcessor without using MediatR, just using F# language features:
// Define the request type.typeSayHelloRequest={Name:string}// Define the event type.typeUserGreetedEvent={Name:string}// Request handling function.letsayHelloHandler(request:SayHelloRequest)=printfn"Hello %s"request.Name// Event handling function.letuserGreetedHandler(event:UserGreetedEvent)=printfn"User %s was greeted"event.Name// Use a pipeline to send a request.letsendRequestname={Name=name}|>sayHelloHandler// Use event subscription with `MailboxProcessor`.letpublisher=MailboxProcessor.Start(funinbox->letrecloop()=async{let!event=inbox.Receive()userGreetedHandlereventreturn!loop()}loop())// Publish an event.letpublishname=publisher.Post({Name=name})// Orchestrator.letorchestratorname=sendRequestnamepublishnameorchestrator"John"
Listing other methods:
For example, you can use:
- The Event type:
typeEvent<'T>=delegateofsender:obj*evt:'T->unit
Then define the event handling function as a delegate of this type:
lethandler(s,e)=printfn"Handling event: %A"e
Subscribe to the event:
letevent=Event<UserGreetedEvent>()event.Publish(handler)
Publish the event:
event.Trigger(this,{Name="John"})
- Rx (Reactive Extensions) can use Rx's Subject to replace events:
letsubject=newSubject<'T>()subject.OnNext(event)|>ignoresubject.Subscribe(handler)
- Agent can use Agent's Post and Receive for message passing:
letagent=Agent.Start(funinbox->letrecloop()=async{let!msg=inbox.Receive()handlermsgreturn!loop()}loop())agent.Post(event)
End of the article, discussion is welcome.
The code is for reference only. Please modify it yourself if you want to run it.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse