Skip to content

Advanced

Mature Dependency Injection Containers

Mediator allows for a lot of complex generic registration and resolution via covariance. Below is a list of our current & known support. We do not intend to test any other containers at this time.

ContainerInformation
Microsoft.Extensions.DependencyInjectionCan get many of the simple cases that are required by our HTTP & Prism modules, but will break down for more complex scenarios like our Dapper extension
DryIocFully Supported
AutofacFully Supported

Event Collectors

What are they?

Event collectors allow you to bring in event handlers that may not be participating in the dependency injection lifecycle or perhaps they simply have a different scope outside of where you are IN the DI lifecycle.

Traditionally, in classic Xamarin Forms and even some .NET MAUI apps tend not to register their viewmodels or pages with the DI lifecycle, however, all of these apps participate in the navigation stack. Thus, we have an event collector for MAUI that simply traverses to find any pages or viewmodels that implement the IEventHandler at the time of the event publish. What this means is that if you pop a page/viewmodel off the stack before the publish is called, it will no longer participate in the event collector.

Creating an Event Collector

public class MyEventCollector : IEventCollector
{
public IReadOnlyList<IEventHandler<TEvent>> GetHandlers<TEvent>() where TEvent : IEvent
{
// return any handlers in your required scope
}
}

Registering Event Collectors

builder.Services.AddShinyMediator(cfg =>
{
cfg.AddEventCollector<MyEventCollector>();
});

MAUI & Blazor Event Collectors

We offer two event collectors out of the box for MAUI (Shiny.Mediator.Maui) & Blazor (Shiny.Mediator.Blazor).

Our MAUI event collector will traverse the navigation stack to find any pages or viewmodels that implement the IEventHandler interface. This is a great way to participate in the event chain without having to worry about DI.

Our Blazor event collector has a component factory that holds a weak reference to any component created that implements the IEventHandler interface.

Project Structure

Project structure is an important to helping with large applications. This is where ‘vertical slicing’ and cross team implementation really gets good!

Reasons for this Engineering

Allows cross functional teams to work enable cross functional requirements without being tightly coupled Allows each feature/module to register its own services, controls, pages, viewmodels, etc in isolation while still having access to shared functionality like logging, configuration, etc Through use of Shiny.Mediator.Prism, we can achieve strongly typed routing and navigation parameters via a simple Mediator request that implements the IPrismNavigationRequest

Module Layout Template

diagram
ProjectDescription
TheAppThis is the main MAUI executable project
SharedThis is where any shared services, utils, etc go to share amongst ALL modules
PeopleModuleWhere all the handlers, views, viewmodels, and services specific to people live
PeopleModule.ContractsContracts that the PeopleModule owns and manages
VehicleModuleWhere all the handlers, views, viewmodels, and services specific to vehicles live
VehicleModule.ContractsContracts that the VehicleModule owns and manages
OwnerModuleWhere all the handlers, views, viewmodels, and services specific to owners live
OwnerModule.ContractsContracts that the OwnerModule owns and manages

To see this runnable sample code, please go to [https://github.com/shinyorg/mediatorsample/]

Advanced Dependency Injection

Dependency Injection is pluggable as we use the Microsoft.Extension.DependencyInjection setup to provide our servicing. That being said, your mileage will vary depending on what your container will support. We test our apps with

  • DryIoc
  • Microsoft Dependency Injection

If you use DI containers outside of this, you will need to do your own test cases!

One of the big value adds for middleware & events is that they support covariance.

public record ChildEvent : IEvent;
public record ParentEvent : ChildEvent;
public record GrandParentEvent : ParentEvent;
public class AllFamilyEventHandler : IEventHandler<GrandParentEvent>
{
public async Task Handle(GrandParentEvent @event, EventContext<GrandParentEvent> context, CancellationToken cancellationToken)
{
// this can actually be
}
}
var services = new ServiceCollection();
services.AddSingleton(typeof(IEventHandler<>), typeof(AllFamilyEventHandler));

Additional Registration Methods (Out of the Box)

We have several methods to help deliver easier registration experiences

You may have an event handler or request handler that handles multiple commands, but you want this “service” to hold state. Thus, we offer some simple methods to hold that state in scope of a request or publish.

using Shiny.Mediator;
using Microsoft.Extensions.DependencyInjection;
public class YourImplementation : IEventHandler<SomeEvent>, IEventHandler<SomeOtherEvent> {
// .. left empty for brevity
}
var services = new ServiceCollection();
services.AddSingletonAsImplementedInterfaces<YourImplementation>();
services.AddScopedAsImplementedInterfaces<YourImplementation>();
services.AddShinyMediator();
var services.BuildServiceProvider();
var e1 = sp.GetRequiredService<IEventHandler<SomeEvent>>();
var e2 = sp.GetRequiredService<IEventHandler<SomeOtherEvent>>();
e1 == e2 // same instance