With the new MauiAppBuilder comes a new way to create applications in .NET, using MauiApp.CreateBuilder(). In this post I compare this approach to previous approaches, discuss why the change was made, and look at the impact. In the next post, I will see the code behind MauiApp and MauiAppBuilder see how they work.
This article belongs to the series: Exploring MAUI App Builder. In this series, we’ll talk about some new features included in .NET MAUI.
Built .NET Applications Today
In order to make a fair comparison, we must bring to the table the predecessors, or at least the source, from where the idea of creating the MauiAppBuilder in .NET 6 with .NET MAUI comes from.
Before we look at .NET 6, I think it’s worth looking at the process of ASP.NET Core 3 applications since this design is built on top of the generic host today. With this in mind, we can show 3 paradigms:
- Host.CreateDefaultBuilder(): Built on top of the generic ASP.NET Core Host.
- WebApplication.CreateBuilder(): The new host for web applications in .NET 6.
- MauiApp.CreateBuilder()– The new host for cross-platform applications with .NET MAUI in .NET 6.
To get a better idea of the differences, I’ve reproduced the typical “startup” code in the following sections, which should make the changes shown more apparent.
ASP.NET Core 3.x/5: the generic HostBuilder
ASP.NET Core 3.x brought some big changes to the startup code for ASP.NET Core. Previously, ASP.NET Core could only really be used for web/HTTP workloads, but in .NET Core 3.x a move was made to support other approaches: long running “worker services” (for consuming message queues, for example), gRPC services, Windows Services, and more. The goal was to share the base framework that was built specifically for building web apps (configuration, logging, DI)with these other app types.
The upshot was the creation of a “Generic Host” (as opposed to the Web Host), and a “re-platforming” of the ASP.NET Core stack on top of it. Instead of an IWebHostBuilder, there was an IHostBuilder.Series: Exploring ASP.NET Core 3.0 (andrewlock.net)
With these changes and the hard work of the ASP.NET team, changes such as Endpoint Routes were brought to the table. Routing was one of the changes that in previous versions was limited and that today is one of the most striking things within ASP.NET Core.
With all the changes that were made in ASP.NET Core 3 with Razor Pages, we can have a Startup class similar to this:
... public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } } ...
Within ASP.NET Core 5 there were a few major changes so the configuration is similar.
In .NET 6 there is support for the configuration of the host in its previous version but there are some changes in the Startup class …
ASP.NET Core 6: WebApplicationBuilder
With all updates of C# 10, BCL, and ASP.NET Core now in .NET 6, we have a new style where we can have everything in one place.
... var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorPages(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.MapGet("/", () => "Hello World!"); app.MapRazorPages(); app.Run(); ..
Here you can see the changes, below the most notable:
- Top-level declarations mean that Program.Main()is not repeated.
- Implicit usage policies mean that no instructions for use are required.
- No Startup class: everything is in one file.
In the example above there is much less code, but is it necessary?
The reason for everything
One of the things that .NET 6 has focused on is “beginners.” As a beginner in ASP.NET Core, there are many concepts that must be understood before going deeper.
Updates in .NET 6 focus on eliminating the “initialization” associated with startup and hiding concepts that can be confusing for beginners.
For example:
- Using statements are not required when starting out.
- Similar to namespaces,it’s an unnecessary concept when you start.
- Program.Main() Bring more questions than answers.
- The configuration is not split between two files, Program.cs and Startup.cs.
- Etc…
In addition, we have the new WebApplication and WebApplicationBuilder types. These types weren’t strictly necessary to achieve the above goals, but they make the setup experience somewhat “cleaner.”
.NET Maui: MauiAppBuilder
With the evolution of Xamarin to .NET MAUI (Multi-platform App UI), we can notice an update that changes the way we initialize our applications completely.
.NET MAUI implements the .NET Host Builder pattern making the start of our applications aligned to ASP.NET and Blazor. This helps .NET developers reuse concepts already learned, which makes the learning curve smaller.
For example the MauiProgram.cs (which I understand should be called Startup.cs)
... public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }); return builder.Build(); } } ...
If we compare the implementation of .NET MAUI with the implementation of ASP.NET Core 6 we can see that it does not enjoy the big updates such as the Top Level Declarations.
When I see that in native projects MauiProgram.CreateMauiApp() is used, I wonder: why is it called that? Shouldn’t it be called Startup? At the end that’s its function, isn’t it?
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
In the official examples of .NET MAUI, you can see how the file and the class are called Startup. Hopefully, these will be the future changes, remember that we are in Preview still.
Can watch it here.
But more importantly than that, why do we need two configuration files? Why do I need it together with App.xaml? Why do I have to have two files to configure my application?
For example the App.xaml.cs
... public partial class App : Microsoft.Maui.Controls.Application { public App() { InitializeComponent(); MainPage = new MainPage(); } protected override Window CreateWindow(IActivationState activationState) { Microsoft.Maui.Controls.Compatibility.Forms.Init(activationState); this.On<Microsoft.Maui.Controls.PlatformConfiguration.Windows>() .SetImageDirectory("Assets"); return new Microsoft.Maui.Controls.Window(new MainPage()); } } ...
In the example, we can see how we are defining both the MainPage and the compatibility for windows. These methods can be easily used from Startup.cs (assuming it will be called that) creating the necessary extensions for this.
Should we be worried?
Well, no, we shouldn’t. We can create our applications following the pattern defined by the .NET MAUI team without any problem.
However, I think you have to agree that we seem to be reproducing the bugs that the .NET 6 WebApplication version has already fixed.
This makes me think about the following:
- As the projects grow we will have a lot of nested lambdas.
- Just like ASP.NET 3 I was using Startup.cs and Program.cs in .NET MAUI we are using Startup.cs and App.xaml.cs.
- We are not taking advantage of the most striking approach of .NET 6 and C# 10 in the templates.
Generally speaking Startup.cs is a procedure boot script (mostly) that solves a lot of problems but is a bit short of being on par with its brother within .NET 6.
It should be noted that one of the benefits of .NET 6 was to simplify the beginnings of new developers and ASP.NET Core 6 WebApplicationBuilder simplifies many things for them.
We must remember that .NET MAUI is still in preview, let’s wait for the new news related to this section.
Most of the configuration occurs in MauiAppBuilder
Let’s start by looking at the MauiAppBuilder.
var builder = MauiApp.CreateBuilder();
MauiAppBuilder is responsible for 4 main things:
- Add settings using builder. Configuration.
- Add services using builder. Services.
- Configure logs using builder. Logging.
- Generate builder configuration. HostBuilder.
Let’s see them one by one…
ConfigurationManager
MauiAppBuilder exposes the ConfigurationManager type to add new configuration sources, as well as to access configuration settings, as I showed in my previous post.
ServiceCollection
It also exposes direct IServiceCollection to add services to the DI container. So, while with the generic host you could do something like this:
... var builder = MauiApp.CreateBuilder(); builder.ConfigureServices(services => { services.AddSingleton<MainPage>(); services.AddSingleton<MainPageViewModel>(); }); ...
With MauiAppBuilder you can:
var builder = MauiApp.CreateBuilder(); builder.Services.AddSingleton<MainPage>(); builder.Services.AddSingleton<MainPageViewModel>();
Loggin
The same way, for logs, instead of doing:
... var builder = MauiApp.CreateBuilder(); builder.ConfigureLogging(logging=> { logging.AddFile(); // this is just an example }); ...
You can do something similar to this:
var builder = MauiApp.CreateBuilder(); builder.Logging.AddFile(); // this is just an example
This has exactly the same behavior, only in a more user-friendly API. For those extension points that depend on IHostBuilder, the Host property is directly exposed.
For example, when libraries start using this pattern and its benefits we can use the MauiAppBuilder with UseLibrary() to call the Host property, for example.
builder.Host.UseLibrary();
So, MauiAppBuilder is where you should do all your settings except for those made in App.xaml.cs.
MauiApp
Once you have configured everything you need in MauiAppBuilder you can call Build() to create a MauiApp instance.
... public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder.UseMauiApp<App>(); return builder.Build(); } ...
For people new to MAUI they will find the UseMauiApp() method called the App class that is the one created by our application.
Summary
In this post, I described how booting mobile applications, now with .NET MAUI, has changed in .NET 6.
I show the new MauiApp and MauiAppBuilder types introduced in .NET 6, discuss why they were introduced, some of the advantages they bring, and discuss some suggestions.
Finally, I discuss the different roles the two classes play and how their APIs make the startup experience simpler. In the next installment, we will see some interesting things.
If you still do not follow me on the networks, you can do it on Twitter or Linkedin. It will be great to see you over there.