-
Notifications
You must be signed in to change notification settings - Fork 12
2 Using the dependency registration source generator
With Maui.Plugins.PageResolver, you can automatically register all of your dependencies (Pages, ViewModels, Services and associated interfaces) with a source generator.
The source generator is conventions based, so make sure you follow the conventions for it to work as expected.
The source generator will scan your project and automatically register dependencies for you, and because it is a source generator, the code is included at compile-time, which has no runtime impact (compared to, say, something using reflection).
Follow the instructions below to simplify dependency registration, use PageResolver, and remove a whole load of common boilerplate code from your .NET MAUI projects.
Once you have completed these steps, use the PageResolver as per the instructions in step 1.4 of the using PageResolver instructions.
The source generator currently only works for single-project .NET MAUI apps. If you have created additional class libraries for your Pages, ViewModels or Services, you will need to manually register these dependencies in code.
The source generator is included in the main PageResolver package, so ensure you have this installed:
dotnet add package Goldie.MauiPlugins.PageResolverInstead of calling the UsePageResolver extension method, call UseAutodependencies:
namespace MyApp
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
})
.UseAutodependencies();
// YOU DON'T NEED THESE ANYMORE
// builder.Services.AddSingleton<IMyService, MyService>();
// builder.Services.AddTransient<MyViewModel>();
// builder.Services.AddTransient<MyPage>();
// builder.Services.UsePageResolver();
return builder.Build();
}
}
}That's it! If you've followed these steps, and the conventions below, all of your Pages, ViewModels and Services (and their associated interfaces) will be automatically registered on build, and you can use await Navigation.PushAsync<MyPage>(); without any further effort.
Note that the UseAutodependencies extension method is created by the source generator itself, so if you get an error when trying to use it try to rebuild your solution first.
PageResolver will automatically register Pages, ViewModels and Services for you based on the following conventions.
You can still use it without following the conventions, but anything you want to register in the DI container that does not follow these conventions, you will need to register yourself.
Any class with a class name that ends with Page will be automatically registered with the DI container.
All pages are registered with TRANSIENT scope by default (see overriding conventions below).
Examples:
| Files | Class name | ||
|---|---|---|---|
| ✔ | MainPage.xaml, MainPage.xaml.cs | MainPage | <-- This will be registered |
| ✔ | ProductPage.cs | ProductPage | <-- This will be registered |
| ❌ | ProductView.xaml, ProductView.xaml.cs | ProductView | <-- This will NOT be automatically registered |
Any class with a class name that ends with ViewModel will be automatically registered with the DI container.
All ViewModels are registered with TRANSIENT scope by default (see overriding conventions below).
Examples:
| Files | Class name | ||
|---|---|---|---|
| ✔ | MainPageViewModel.cs | MainPageViewModel | <-- This will be registered |
| ✔ | ProductViewModel.cs | ProductViewModel | <-- This will be registered |
| ❌ | ProductModel.cs | ProductModel | <-- This will NOT be automatically registered |
Any class with a class name that ends with Service will be automatically registered with the DI container.
All Services are registered with Singleton scope by default (see overriding conventions below).
For interfaces with a name that matches the pattern I[...]Service, services are registered as an implementation of they interface they match.
Let's say you define an interface called IMappingService, and create a class that implements it called MappingService. These will be automatically registered for you:
services.AddSingleton<IMappingService, MappingService>();Let's say you have a class in your project called NetworkService, but it does not implement any interface, this will be automatically registered for you:
services.AddSingleton<NetworkService>();This can still be injected into the constructor of your Pages or ViewModels:
public MyViewModel(NetworkService networkService)
{
...Let's say you have an interface called IPaymentService, and you have two classes that implement it: StripeService and MockPaymentService. Both of these will be registered automatically, but neither will be registered as an implementation of the interface:
services.AddSingleton<StripeService>();
services.AddSingleton<MockPaymentService>();You will need to register these implementations yourself in code.
By default, Pages and ViewModels will be registered as Transient, and Services will be registered as Singelton. You can override these by decorating any class with the [Singleton], [Transient], or [Ignore] attributes (these are in the Maui.Plugins.PageResolver.Attributes namespace).
Any Page, ViewModel, or Service decorated with the [Singleton] or [Transient] attributes will be explicitly registered according to the attribute, as in the following example.
[Singleton]
public class ProfilePage : ContentPage
{
}[Transient]
public class AuthenticationService : IAuthServiceThis will generate:
// pages
builder.Services.AddSingleton<ProfilePage>();
//services
builder.Services.AddTransient<AuthenticationService, IAuthService>();It may be necessary to not register certain types with the container, for example abstract classes that match the naming pattern, or excluding mock dependencies used only for scratch testing. Any Page, ViewModel, or Service decorated with the [Ignore] attribute will not be registered, as in the following example.
public class PaymentPage: ContentPage
{
}[Ignore]
public class MockPaymentService: IPaymentService
{
//...
}public class PaymentService: IPaymentService
{
//...
}This will generate:
// pages
builder.Services.AddTransient<PaymentPage>();
//services
builder.Services.AddSingleton<PaymentService, IPaymentService>();
// MockPaymentService is not registered
// Without using ignore, it would be registered like so:
// builder.Services.AddSingleton<MockPaymentService>();