Structuring a .NET Core application – Part 2

Inversion of Control

Before you begin reading this article, please take your time and read part 1 first: Structuring a .NET Core application – Part 1: Separation of Concern.

When building a service that has a dependency to a second, third or even fourth service. A sensible decision would be to just create new instances of those dependencies as you need them. It makes sense. I need a price calculation service therefore I create one and use it.

It makes sense to do so. Only create what you need when you need it. But what will happen if you need the price calculation service multiple places. You will end up creating multiple instances of the same service, and that could impact performance in a bad way. One way of handling this, is to add the dependent service as a parameter to the constructor and let someone else handle the creation of the dependent service. That is called Inversion of Control or IoC.

This post is not about IoC. But about how we can use that to our benefit by moving the creation and disposal of services away from our services into an IoC container. Luckily for us, such a container is baked into ASP.NET Core.

I will in this post show how to easily use IoC and register multiple services at once and use the same registration method in different .NET Core application types. From ASP.NET and Azure Functions to Console and Universal Windows applications. And yes, it is possible and quite powerful.

The Visual Studio Solution

To illustrate what we’re doing, I’ll have a solution with 7 projects:

  1. IoCTest.Web.Rest (ASP.NET)
  2. IoCTest.AzureFunctions.EmailService (Azure function)
  3. IoCTest.Applications.CLI (console app)
  4. IoCTest.Applications.Windows (uwp app)
  5. IoCTest.Integrations.Email (.NET Standard library)
  6. IocTest.Infrastructure.DAL (.NET standard library)
  7. IoCTest.Integrations.AzureStorage (.NET Standard library)

It might seem like a lot, but you’ll see, once you’ve done it once, you’ll be able to do them all. In an extraordinary short time. The solution here, took me about an hour and a half to setup. Yes, it has no business logic, but it does have a lot of moving parts. 1 hour 30 minutes from zero to 7 projects all referenced, IoC initialized, setup and ready to go.

The source code for this post can be found on GitHub right here:

https://github.com/nickfrederiksen/net-core-structure/tree/part-2-inversion-of-control

With ASP.NET Core came a build-in inversion of control container wrapped in a service called WebHostBuilder. This was meant for ASP.NET and ASP.NET only. But with .NET Core 3, came the new GenericHostBuilder. With this update came the ability to take many of the features available to ASP.NET and use them in new application types. It even makes it possible to mix and match. Hosting a rest api in a console app. Having console features in a web app, even Windows services. But that is out of scope for this series. For now, we will focus on one thing: Inversion of Control.

Just a side note: You can get the pattern I present her to work from a .NET Framework application using a DI like Ninject, Castle Windsor or LightInject. It’s a bit more cumbersome, and I won’t get into that since .NET Framework has a foot in its grave already.

Registering the services

Using the build-in service locator we need to register the services. That is done using the IServiceCollection interface from the host builder.

In an ASP.NET Core application, this is done in the Startup.cs class in the method “RegisterServices”. And it looks a lot like this:

ASP.NET Core, (Startup.cs):

private void RegisterServices(IServiceCollection services)  
{  
    services.AddScoped<IEmailClient, EmailClient>();  
}

Azure Function, (Startup.cs):

public override void Configure(IFunctionsHostBuilder builder)  
{  
    builder.Services.AddSingleton<IEmailClient, EmailClient>();  
}

Console application, (Program.cs)

builder.ConfigureServices((hostContext, services) =>  
{  
    services.AddSingleton<IEmailClient, EmailClient>();  
});  

Universal Windows Application, (App.xaml.cs)

IServiceCollection services = new ServiceCollection();  
  
services.AddScoped<IEmailClient, EmailClient>();  

As I wrote earlier, the inversion of control container is native to ASP.NET Core but we can make use of it in difference application types as well. I will demonstrate that later on.

This way of registering services is easy. Each application registers the services it needs and we are all happy. Or are we? If we look at the EmailClient, we can see it has two dependencies:

public EmailClient(  
    IEmailBodyGenerator bodyGenerator,  
    SmtpClient smtpClient)  
{  
    this.bodyGenerator = bodyGenerator;  
    this.smtpClient = smtpClient;  
}  

This means, that each application must register each of those services as well. And when the dependencies change, and it will in part 3 of this series, we will have to update each application and essentially copy and pasting code, creating duplicate code and that is bad. See part 1 on that.

What if we could make our email library do the work for us? The library ought to know about all its services and their dependencies? I think that’s a fair assumption.

When I build libraries like this, I like to make a Configurator class. What this class does is actually the same as you would in the above examples.

All you need to do is install the following NuGet package:

  • Microsoft.Extensions.DependencyInjection.Abstractions

However, if your library has a nuget package installed, that already has this dependency. You don’t need to install this one. That is the case with the DAL library, where the Entity Framework Core package has a dependency on this package already.

The configurator class has a static method, an extension method for IServiceCollection, and looks a bit like this.

public static void AddEmailServices(this IServiceCollection services)  
{  
    services.AddScoped<IEmailClient, EmailClient>();  
    services.AddScoped<SmtpClient>();  
    services.AddScoped<IEmailBodyGenerator, HtmlGenerator>();  
}

Note the naming. The method is named after what it does. This one adds all the services needed by the email library. If your library has multiple, independent services, you could add more of these extension methods. That’s up to you.

Now all we have to do in our startup.cs and their equivalents are this:

services.AddEmailServices();

And voilà, now the email services can evolve by themselves, all without breaking your applications or forcing you to update all the registrations.

I have come across multiple solutions where this is the case, been involved in quite a few of them myself before learning this “trick”. Which is why I choose to write about it here.

I know that it creates a dependency on a NuGet package on all our libraries, but I find the trade-off worth the while. Since we have separated the concerns into separate libraries, each with their own setup method, we have less duplicated code and decreased complexity. And we can reuse the same setup logic across several types of applications as well.

Feel free to go through the repo above to get into details on how I’ve implemented this.

Please bear in mind, that this is only for demonstration, not for production. The code builds and runs, but it needs a lot of configuration which I’ll dig into in the next part, and still has no business logic.

For now, let’s wire up the inversion of control containers for each of our applications:

Setting up Inversion of Control

As you know by now, ASP.NET Core already comes with an IoC container baked in so I won’t get into details on that. But I will take you through the other application types.

Console application

If you open up the IoCTest.Web.Rest project, you’ll see a Program.cs file. At first glance, it might seem odd that a web application needs a Program class just like a Console application. The reason is that an ASP.NET Core application, first and foremost, is a console app.

If you open the bin folder, you’ll see that is has created two files: IoCTest.Web.Rest.dll and IoCTest.Web.Rest.exe. The dll contains the code that we need to actually run the application. The exe file is the self-hosted application. A portable webserver that serves your application, just like IIS would do. If you run the .exe file, you’ll see something like this:

Example of an ASP.NET Core application running as a console application.
Example of an ASP.NET Core application running as a console application.

You can now go to http://localhost:5000 or https://localhost:5001. All this without setting up a local IIS or starting up an IIS Express instance.

This, to me, demonstrates perfectly, that an ASP.NET Core application is just a simple console application, with some services loaded and executed. And that opens for a whole lot of awesome.

If you think about it. If you can load up a web server from a console app. What kind of other stuff can you load up as well?

So, let’s look at our IoCTest.Applications.CLI project.

It has two files: HostedConsoleService, which we’ll look at later, and a Program file, pretty standard for a console application.

If you look at the Program.cs file from both the ASP.NET application and the console application side by side, you’ll notice a couple of things:

  1. They both call Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
  2. They both configure services, (the web app through the startup class)
  3. And they both call a Run method.

When you create a new .NET Core console app, you will not have access to the host builder. You will, in fact, have access only to a bare minimum of services for a .NET Core application. We need to install two NuGet packages to get that:

  • Microsoft.Extensions.Hosting
  • Microsoft.Extensions.Hosting.Abstractions

This will give us access to the generic host builder and the IHostedService interface, which I will dig into later.

All we need to do is to change our Main method a bit:

static async Task Main(string[] args)  
{  
    var builder = Host.CreateDefaultBuilder(args);  
    builder.ConfigureServices((hostContext, services) =>  
    {  
        services.AddSingleton<IHostedService, HostedConsoleService>();  
                  
        // TODO: Register services  
    });  
  
    await builder.RunConsoleAsync();  
}  

You’ll notice that I’ve change the signature a bit. With .NET Core 2.0 we finally got support for native async console applications. Which we need since our application host is going to run asynchronously.

The second thing you’ll see, is that I call Host.CreateDefaultBuilder(), exactly as I do in the ASP.NET Core application.

It then deviates a bit.

Instead of ConfigureWebHostDefaults, we call ConfigureServices. And instead of calling .Build().Run(), we call RunConsoleAsync().

The ConfigureServices method is our stand-in for the startup class. This is where we’ll register all our services. Including, the most important one: The IHostedService implementation HostedConsoleService.

Let’s dig into that one.

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

Nothing. Absolutely nothing. But it’s still very important.

In a normal console application, the Main method is our entry point. This is where we instantiate and run everything. But not in this case. In this case, we register our services and starts a generic host from our Main method. But our host is no good host if it has no services to host. Hence the IHostedService. This class is now our entry point. We can create a constructor and add all the services we need like this:

private readonly IEmailClient emailClient;  
  
public HostedConsoleService(IEmailClient emailClient)  
{  
    this.emailClient = emailClient;  
} 

And then we can use the IEmailClient service. Of course, we need to register it first, but you get the gist.

A note: Normally a console app closes automatically after completion. Not in this case. We need to tell our application to stop. To do so, just inject the Microsoft.Extensions.Hosting.IHost interface and call the host.StopAsync() method when you need to stop the application. Otherwise it keeps running until ctrl+c is pressed, or the console is closed.

A second note: In a web context, a scoped service is created once in the beginning of a request and reused until the request ends. In console applications and UWP applications, you need to manage the scopes yourself. I will not dive into this here, as it is out of scope for this series.

Azure Functions

From the very beginning, Azure Functions was build almost the same way as basic console applications: A static class with a static method with your code. It’s all good for small tasks. But if you have a set of methods doing different tasks on the same libraries it quickly gets complicated when you have to initialize every single service one by one, for every dependent service as well. And doing it for multiple Azure Functions. There was some rudimentary dependency injection, but only for a few known services and definitely not for custom services.

This has changed with .NET Core 3 and the Azure Functions v3 API. Now we can have a startup.cs and use the approach described earlier on.

All we need is two NuGet packages:

  • Microsoft.Azure.Functions.Extensions
  • Microsoft.Extensions.DependencyInjection

The latter, I find, is self-explanatory. The former, is what makes magic possible. It exposes an abstract class called, Microsoft.Azure.Functions.Extensions.DependencyInjection.FunctionsStartup.

All we have to do now, is to create a new class, Startup.cs, inherit from this class and implement the Configure method.

Oh, and one thing: We need to tell the runtime that we have a startup class, otherwise it won’t use it. To do that, we need to add this to our class file:

[assembly: FunctionsStartup(typeof(IoCTest.AzureFunctions.EmailService.Startup))]  
namespace IoCTest.AzureFunctions.EmailService  

The FunctionsStartupAttribute is found in the namespace Microsoft.Azure.Functions.Extensions.DependencyInjection.

It is important, that this line of code is located outside of the namespace as it contains information that is needed by the runtime during assembly load.

All it does, is telling the runtime to create an instance of the class registered and start the application from here.

The Configure method, gives us access to an instance of IFunctionsHostBuilder. At the moment, it doesn’t contain much, but it do contain a single property: Microsoft.Extensions.DependencyInjection.IServiceCollection Services { get; }.

And you will notice, that the IServiceCollection matches the one we have been using to register all our services. So now, we can do something like this:

public override void Configure(IFunctionsHostBuilder builder)  
{  
    var services = builder.Services;  

    services.AddEmailServices();  
    services.AddDatabaseAccess();  
    services.AddStorage();  
} 

Brilliant! But how can we inject these fine services into our static Function class. Easy: First remove the “abstract” keyword from the class and method definition. Add a constructor that takes the services you want to use, and the use them in your function:

public class Function1  
{  
    private readonly IEmailClient emailClient;  
  
    public Function1(IEmailClient emailClient)  
    {  
        this.emailClient = emailClient;  
    }  
  
    [FunctionName("Function1")]  
    public Task Run([TimerTrigger("0 */5 * * * *", RunOnStartup = true)]TimerInfo myTimer, ILogger log)  
    {  
        return emailClient.SendEmailAsync("[email protected]", "[email protected]", "Sent from a Azure Function using IoC", new { text = "This is so awesome!" });  
    }  
}  

Simple as that.

As you can see, three different application types, all using the same service registration logic. And now for something a bit more complicated:

Universal Windows Application

I have little to none experience with Universal Windows Applications, or UWP Apps. I did a bit of WPF development back in the day, but it has definitely been a long time since.

This example is merely to demonstrate how to use the same methodology as above, but in a completely different environment. UWP apps are not console applications. They work in a completely different way compared to web applications and are not service based as Azure Functions. I will not dig into scope management as I’m too unfamiliar with the API’s to do so. But I will show how you could build inversion of control into you UWP app and reuse the same services as your other applications. Without any hazzle, well maybe a little, let’s see:

First you need to install three NuGet packages:

  • Microsoft.Extensions.DependencyInjection
  • Autofac
  • Autofac.Extensions.DependencyInjection

Since UWP’s, as of writing, does not have inversion of control natively, we need to use an external IoC container. In this case, Autofac. And you’ll see why, later.

As you might have noticed, I like to separate things into separate parts by concern. The same goes for this next part.

Any application has an entry point. UWP apps has one called App.xaml and a corresponding App.xaml.cs. We want to extend this a bit. So, first things first: Create a new file called App.Services.cs. In this file, you should create a single partial class called “App”:

public partial class App  
{  
  
}

In here we’ll add a public static property that contains the IoC container and a method called ConfigureServices().

In here, we’ll setup Autofac and register our services:

public partial class App  
{  
    public static IContainer ServiceContainer { get; private set; }  
    private void ConfigureServices()  
    {  
        var containerBuilder = new ContainerBuilder();  
        IServiceCollection services = new ServiceCollection();  
  
        services.AddScoped<IEmailClient, EmailClient>();  
  
        this.ConfigureServices(services);  
  
        containerBuilder.Populate(services);  
  
        ServiceContainer = containerBuilder.Build();  
    }  
    private void ConfigureServices(IServiceCollection services)  
    {  
        services.AddEmailServices();  
        services.AddDatabaseAccess();  
        services.AddStorage();  
    }  
}  

That’s it really. Remember to include these namespaces: Autofac, Autofac.Extensions.DependencyInjection and Microsoft.Extensions.DependencyInjection.

Now we only need to call our “ConfigureServices” method from the App class and we are golden. For this example, I do it from the constructor. You might want to do it somewhere else to better manage resources when going back and forth between pages and the suspended state. You might know this a lot better than I.

To get one of your services, all you have to do is call the Resolve<> method on the App.ServiceContainer property:

public sealed partial class MainPage : Page  
{    
    public MainPage()  
    {  
        var emailClient = App.ServiceContainer.Resolve<IEmailClient>();  
  
        this.InitializeComponent();  
    }  
} 

You’ll notice that we don’t have dependencies in the constructor. At the time of writing, it is very complicated to get that working, so this method will do for now.

You can read more about Autofac and how to register and resolve services here:

https://autofaccn.readthedocs.io/en/latest/resolve

In the next part, I will dig into how to use all of this for configuration. Allowing the same services to be configured from completely different configuration sources. It is almost scary how easy and reusable it is.

TL;DR

It is very easy to build a large number of small services and reuse these services across different kinds of .NET Core applications. By using IoC intelligently.

It is also easy to maintain all these small services, registering them, updating, adding and removing them. Without breaking all your applications with one standardized method.

It might take a bit of configuration for some of the application types, but it is all “set and forget”. Have you done it once, you will never have to think about it ever again.

Read more

If you haven’t already, please take a look at the other posts in this series:

Structuring a .NET Core application – Part 1: Separation of Concern