Using Reflections to Automate registering your MAUI viewModels and pages to the dependency Service container.
It's been a while now since the MAUI rewrite started and I am happy that MAUI GA has finally been released.
I like all the new features they have implemented out of the box to help speed up the developer cycle when working with MAUI.
One of the things which I do and I really find annoying with Xamarin was setting up a dependency container ,and having to bootstrap stuff ,it was just daunting but now MAUI provides a built in Dependency container, that looks and works just like in ASPNET.CORE ,great right?! But now to be able to inject the services you have to register everything including the pages you create manually ,this repetitive labour was starting to kill my excitement Dev exp and my MAUIProgram Class was getting bloated.
Here is a quick example of how your typical MAUI program class would look like.
builder.Services.AddSingleton<ISomeProductService,SomeProductService>();
builder.Services.AddSingleton<MainPage>();
builder.Services.AddSingleton<ProductsPage>();
builder.Services.AddSingleton<ProductDetailPage>();
builder.Services.AddSingleton<AboutPage>();
builder.Services.AddSingleton<AboutPageViewModel>();
builder.Services.AddSingleton<ProductDetailPageViewModel>();
builder.Services.AddSingleton<ProductsPageViewModel>();
builder.Services.AddSingleton<HomePageViewModel>();
And this is just a small example.The processes of going back and fourth registering theses classes manually is frustrating.
Luckily The C# Guru is here and I have a quick trick ancient trick that involves dynamic programming in C# that we are going to use to automate this process. Using reflections we
are going to scan and collect all the classes and pages we want to register to our Dependency container then register them all.
*Lets get started *
Firstly we are going to create an interface that we will use as a signature for a class we want to register with the singleton scope in our container.
We are going to call this interface IsingletonDependency, any service that we want to register as a singleton service will have to implement this interface.
The interface will look like this:
public interface ISingletonDependency
{
}
2.
Secondly we are going to add an extension method to attach additional behaviour to MAUIBuilder.
This extension method will encapsulate all the dynamic programming logic and this is the only method that will be called to register our services in the MAUIProgram.cs class.
We are going to create a static class called MauiAppBuilderExtensions that looks like this :
public static class MauiAppBuilderExtensions
{
public static void ConfigureServices(this MauiAppBuilder builder)
{
RegisterSingletonServices(builder);
}
}
next in the same class we are going to create a static method called RegisterSingletonServices this method will scan the assembly and collect all the classes that implement IsingletonDependency then its going to register each class type to the dependency container.
this method will like this :
private static void RegisterSingletonServices(MauiAppBuilder builder)
{
var services = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
.Where(x => typeof(ISingletonDependency).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract).ToList();
foreach (var service in services)
builder.Services.AddSingleton(service);
}
The full extension looks like this :
public static class MauiAppBuilderExtensions
{
public static void ConfigureServices(this MauiAppBuilder builder)
{
RegisterSingletonServices(builder);
}
private static void RegisterSingletonServices(MauiAppBuilder builder)
{
var services = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
.Where(x => typeof(ISingletonDependency).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract).ToList();
foreach (var service in services)
builder.Services.AddSingleton(service);
}
}
That's it we almost done.
The next step is to call this extension method in the MAUI Program class.
builder.ConfigureServices();
The full MAUIProgram class will now look like this :
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.ConfigureServices();
return builder.Build();
}
Its Very Clean right ?!!! Yes it is.
Now you might be probably asking yourself how the viewModel class will look like, it would look like this :
//View model class example
public partial class LoginPageViewModel : ISingletonDependency
{
//Page code behind class example.
public partial class LoginPage : ContentPage,ISingletonDependency
When ever you want to register a class in the container as a singleton you just implement ISingletonDependency interface.
Perfomance ??
I am not an expert in reflections and benchmarking stuff, but well since we using reflections I assume this will obviously have neglect-able startup ms penalty since we have to scan the assembly.
Cheers guys , I hope this will come handy in your projects. I would love to hear you thoughts too..
Top comments (1)
It would be better if you can use your custom attribute eg, "
[SingletonDependency]
"Then you can iterate all the types with the attributes and register them. This answer explains how to get types with the attributes.
Also you can extend the attribute to pass lifetime as parameter to this attribute decoration and register them accordingly. e.g,
[Dependency(Lifetime=Lifetimes.Singleton)]
or[Dependency(Lifetime=Lifetimes.Scoped)]