Hosting

Uno.Extensions.Hosting provides an implementation of the abstraction for building applications which support initialization of dependencies, establishing different environment types, and the full breadth of extensible features offered by Uno.Extensions.

Hosting is delivered as a NuGet package Uno.Extensions.Hosting.WinUI.

Building a Hosted Application

Initialization of the IHost instance is done from the generated App.cs file of your solution. It should be created as soon as the application is launched. The following snippet uses the CreateBuilder() extension method to instantiate an IApplicationBuilder from your Application. It is then possible to configure the associated IHostBuilder to register services or use the numerous extensions offered by this library.

private IHost Host { get; }

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    var appBuilder = this.CreateBuilder(args)
        .Configure(host => {
            // Configure the host builder
        });

    Host = appBuilder.Build();
    ...
}

For a more specific tutorial about getting started with building hosted applications, see Get Started with Hosting.

Service Initialization

Services are the primary way to access application functionality. Services are registered with the host using the ConfigureServices method on the IHostBuilder. Some services need to be created and initialized as soon as possible after the IHost has been built. The IServiceInitialize interface identifies services that need to be created and initialize immediately after the IHost instance is created.

public interface IServiceInitialize
{
	void Initialize();
}
Tip

Avoid using the IServiceInitialize interface unless absolutely required as it will add to the startup time for the application. It's recommended to implement the IHostedService interface for services that can be created and started asynchronously (see next section).

Async Initialization with IHostedService

The initialization of the application hosting is intentionally a synchronous process which makes it unsuitable for long running initialization code. Asynchronous initialization can be done by registering an implementation of IHostedService.

public class SimpleHostService : IHostedService
{
    public Task StartAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }
}

The implementation can be registered during the host initialization by calling the AddHostedService method on the IServiceCollection returned by the ConfigureServices method on the IHostBuilder.

private IHost Host { get; }

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    var appBuilder = this.CreateBuilder(args)
        .Configure(host => {
            host
            .ConfigureServices((context, services) =>
            {
                services.AddHostedService<SimpleHostService>()
            })
        });

    Host = appBuilder.Build();
...

In order for hosted services to be run, it is necessary to run the IHost implementation. It's recommended to do this in the OnLaunched method of the App.cs. This will ensure the application instance and associated window are accessible, should initialization of anything related to the UI be required.

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    ...
    await Task.Run(() => Host.StartAsync());
}

If a hosted service is required to complete startup prior to the first navigation (if using Navigation), implement the IStartupService interface. A task will be returned that can be awaited in the StartupComplete method. This technique might be useful for pre-loading data in order to work out which view to navigate to.

public class SimpleStartupService : IHostedService, IStartupService
{
	private TaskCompletionSource<object> _completion = new TaskCompletionSource<object>();
	public Task StartAsync(CancellationToken cancellationToken)
	{
		_completion.SetResult(true);
		return Task.CompletedTask;
	}

	public Task StopAsync(CancellationToken cancellationToken)
	{
		return Task.CompletedTask;
	}

	public Task StartupComplete()
	{
		return _completion.Task;
	}
}

Hosting Environments

As part of initializing the host, an instance of IHostEnvironment is registered and can be retrieved to determine information about the current hosting environment.

var env = Host.Services.GetService<IHostEnvironment>();
Debug.WriteLine($"Environment: {env.EnvironmentName}");

The current hosting environment can be changed with the UseEnvironment() extension method.

private IHost Host { get; }

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    var appBuilder = this.CreateBuilder(args)
        .Configure(host => {
            host
            .UseEnvironment("Staging")
        });

    Host = appBuilder.Build();
...

The current hosting environment can also be used when configuring the host builder.

private IHost Host { get; }

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    var appBuilder = this.CreateBuilder(args)
        .Configure(host => {
            host
            .ConfigureServices((context, services) =>
            {
                var isDevelopment = context.HostingEnvironment.IsDevelopment();
                var isStaging = context.HostingEnvironment.IsStaging();
                var isProduction = context.HostingEnvironment.IsProduction();
                var environment = context.HostingEnvironment.EnvironmentName;
                var isMyEnvironment = context.HostingEnvironment.IsEnvironment("MyEnvironment");
            })
        });

    Host = appBuilder.Build();
...
Tip

Avoid writing code that contains logic specific to any environment. All environments should behave as close as possible to each other to minimize any environment specific bugs that may be introduced by environment specific code.

Any environment specific secure variables (such as service URLs, application keys, account information) should be set as part of a multi-environment CI/CD pipeline. Non-secure per-environment variables can be included using a settings file which is covered in Configuration.