When handling composition requests, there are scenarios in which request handlers need to offload some of the composition concerns to other handlers.
Note
Composing lists of composed elements or master-details type of outputs is one scenario where events are needed. For an introduction to composing lists and the related challenges, read the Into the darkness of ViewModels Lists Composition blog post.
Events are regular .NET types, classes, or records, like in the following example:
public record AnEvent(string SomeValue);
Events are synchronous, and in memory, they don't need to be serializable.
Publishing an event is done through the composition context, as demonstrated by the following snippet:
public class EventPublishingHandler : ICompositionRequestsHandler
{
[HttpGet("/route-based-handler/{some-id}")]
public async Task Handle(HttpRequest request)
{
var context = request.GetCompositionContext();
await context.RaiseEvent(new AnEvent(SomeValue: "This is the value"));
}
}
ServiceComposer offers two APIs to subscribe to events.
Subscribing to events can be done by creating a class that implements the ICompositionEventsHandler<TEvent>
interface:
public class GenericEventHandler : ICompositionEventsHandler<AnEvent>
{
public Task Handle(AnEvent @event, HttpRequest request)
{
// handle the event
return Task.CompletedTask;
}
}
ICompositionEventsHandler<TEvent>
instances are discovered at assembly scanning time and registered in the DI container as transient components. Hence, event handlers support dependency injection.
A more granular way to subscribe to events is by creating a class that implements the ICompositionEventsSubscriber
:
public class RouteBasedEventHandler : ICompositionEventsSubscriber
{
[HttpGet("/route-based-handler/{some-id}")]
public void Subscribe(ICompositionEventsPublisher publisher)
{
publisher.Subscribe<AnEvent>((@event, request) =>
{
// handle the event
return Task.CompletedTask;
});
}
}
Note
The class must also be decorated with one or more route attributes. Otherwise, it'll never be invoked.
At runtime, when an HTTP request that matches the route pattern is handled, all matching subscribers will be invoked, giving them the opportunity to subscribe. The registered event handler will be invoked when a publisher publishes the subscribed event.
Generic event handlers, classes implementing ICompositionEventsHandler<TEvent>
, are invoked every time an event they subscribe to is published, regardless of the currently handled route. If the same event is used in multiple scenarios, e.g., when doing an HTTP GET and a POST, and different behaviors are required, it's better to implement a route-based event handler that can easily, through the route attribute, differentiate to which type of request it reacts to.