We will try:
- MediatR
- Mailbox
- Event
- Rx
- Agent
MediatR
Example:
open MediatR
// Define the request type inheriting from `IRequest`. Here it's `MyRequest`.
type MyRequest() =
inherit IRequest
// Define the handler that takes the request as a parameter.
let handler (req: MyRequest) =
printfn "Handling request"
// Get the singleton instance of `Mediator`.
let mediator = Mediator.Instance
// Create the request.
let req = 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:
open MediatR
// Define the event type inheriting from `INotification`. Here it's `MyEvent`.
type MyEvent() =
inherit INotification
// Define the event handler function.
let handler (evt: MyEvent) =
printfn "Handling event"
let mediator = 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:
open MediatR
// Define the request.
type SayHelloRequest(name: string) =
inherit Request
member this.Name = name
// Define the handler.
let sayHelloHandler (request: SayHelloRequest) =
printfn "Hello %s" request.Name
// Define the event.
type UserGreetedEvent(name: string) =
inherit Notification
member this.Name = name
// Define the event handler.
let userGreetedHandler (evt: UserGreetedEvent) =
printfn "User %s was greeted" evt.Name
// Request handling process.
let sendHello name mediator =
let request = SayHelloRequest(name)
mediator.Send(request) |> sayHelloHandler
// Event subscription.
let subscribe mediator =
mediator.Subscribe(typeof<UserGreetedEvent>, userGreetedHandler)
// Orchestrator function combining calls.
let orchestrator mediator name =
subscribe mediator
sendHello name mediator
mediator.Publish(UserGreetedEvent(name))
// Get the Mediator and run.
let mediator = Mediator.Instance
orchestrator mediator "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.
type SayHelloRequest = {
Name: string
}
// Define the event type.
type UserGreetedEvent = {
Name: string
}
// Request handling function.
let sayHelloHandler (request: SayHelloRequest) =
printfn "Hello %s" request.Name
// Event handling function.
let userGreetedHandler (event: UserGreetedEvent) =
printfn "User %s was greeted" event.Name
// Use a pipeline to send a request.
let sendRequest name =
{ Name = name } |> sayHelloHandler
// Use event subscription with `MailboxProcessor`.
let publisher = MailboxProcessor.Start(fun inbox ->
let rec loop() = async {
let! event = inbox.Receive()
userGreetedHandler event
return! loop()
}
loop()
)
// Publish an event.
let publish name =
publisher.Post({Name = name})
// Orchestrator.
let orchestrator name =
sendRequest name
publish name
orchestrator "John"
Listing other methods:
For example, you can use:
- The Event type:
type Event<'T> = delegate of sender: obj * evt: 'T -> unit
Then define the event handling function as a delegate of this type:
let handler (s,e) =
printfn "Handling event: %A" e
Subscribe to the event:
let event = Event<UserGreetedEvent>()
event.Publish(handler)
Publish the event:
event.Trigger(this, {Name = "John"})
- Rx (Reactive Extensions) can use Rx's Subject to replace events:
let subject = new Subject<'T>()
subject.OnNext(event) |> ignore
subject.Subscribe(handler)
- Agent can use Agent's Post and Receive for message passing:
let agent = Agent.Start(fun inbox ->
let rec loop() =
async {
let! msg = inbox.Receive()
handler msg
return! 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)