DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,503 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Register Services automatically in MAUI
Nicolas
Nicolas

Posted on • Updated on

Register Services automatically in MAUI

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>();
Enter fullscreen mode Exit fullscreen mode

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
    {
    }
Enter fullscreen mode Exit fullscreen mode

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);
        }


    }
Enter fullscreen mode Exit fullscreen mode

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);
        }

Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
Enter fullscreen mode Exit fullscreen mode

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();
    }
Enter fullscreen mode Exit fullscreen mode

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
    {
Enter fullscreen mode Exit fullscreen mode
//Page code behind class example.
    public partial class LoginPage : ContentPage,ISingletonDependency
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
krishnakumar90 profile image
krishnakumar90 • Edited on

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)]

🌚 Life is too short to browse without dark mode