When it comes to dependency injection and using the built-in IServiceCollection, the framing feels such that we’re restricted to ASP.NET Core. So if we wanted to use IServiceCollection
in console applications instead, what options do we have?
It turns out that we absolutely can! In this article, I’ll touch on one of the Microsoft Learn articles as well as explain an alternative approach… That you can run right in your browser with dotnetfiddle!
Microsoft Learn is a tremendous source of valuable information. I feel like in recent years it continues to surprise me how much valuable information we can find there since I’m used to just relying on MSDN for doc comments and maybe the odd helpful example.
However, in this case, I was a little taken back because the article seemed to be a little bit more cumbersome than I was expecting. If our goal is just to be able to use the IServiceCollection
interface so that we can work with our familiar dependency injection API, I would hope that we can stick to the basics.
However, it looks like the example in the tutorial goes a little bit beyond that and demonstrates the usage of an HostApplicationBuilder
.
Let’s look at the example code, which is taken directly from the Microsoft Learn article:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ConsoleDI.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddTransient<IExampleTransientService, ExampleTransientService>();
builder.Services.AddScoped<IExampleScopedService, ExampleScopedService>();
builder.Services.AddSingleton<IExampleSingletonService, ExampleSingletonService>();
builder.Services.AddTransient<ServiceLifetimeReporter>();
using IHost host = builder.Build();
ExemplifyServiceLifetime(host.Services, "Lifetime 1");
ExemplifyServiceLifetime(host.Services, "Lifetime 2");
await host.RunAsync();
static void ExemplifyServiceLifetime(IServiceProvider hostProvider, string lifetime)
{
using IServiceScope serviceScope = hostProvider.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
ServiceLifetimeReporter logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()");
Console.WriteLine("...");
logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()");
Console.WriteLine();
}
There’s a lot going on here, of course, because they want to use a single example to demonstrate a few different things. However, my biggest challenges with this is that we can see the IServiceCollection
being used via the builder.Services
property call… but why do we need to go make an entire hosted application here?
Now, I’m not claiming that you shouldn’t use a hosted application or anything like that — but I am saying that this feels a little bit heavy-handed. If I just want to use IServiceCollection
then why am I required to build a hosted application?
It turns out that it’s not complex rocket surgery to get this to work without the hosted application pieces from the Microsoft Learn example. That’s important for me because I want to provide people alternatives when I create content about Autofac and they just want to use IServiceCollection like they are familiar with.
Let’s look at this example code which you can see directly on dotnetfiddle:
using System;
using Microsoft.Extensions.DependencyInjection;
public class Program
{
public static void Main()
{
var services = new ServiceCollection();
services.AddSingleton<MyService>();
services.AddSingleton<MyDependency>();
using var serviceProvider = services.BuildServiceProvider();
var myService = serviceProvider.GetRequiredService<MyService>();
myService.DoTheThing();
}
}
public class MyService(
MyDependency _myDependency)
{
public void DoTheThing() =>
Console.WriteLine("Hello World!");
}
public class MyDependency
{
}
Simply, we can create a new service collection by creating a new ServiceCollection
instance:
var services = new ServiceCollection();
Interestingly there was no dependency on having a hosted application or any other type of builder to do this. We can leverage it directly. It’s also important to note in the dotnetfiddle example that I shared that there are no Nugets imported. So we are able to create this instance without additional packages added.
From there, we can start to register our dependencies directly onto the IServiceCollection
(keeping in mind I am only illustrating one type of dependency here):
services.AddSingleton<MyService>();
services.AddSingleton<MyDependency>();
However, this is just the service collection and we still need to create the instance of a service provider that we can resolve dependencies from:
using var serviceProvider = services.BuildServiceProvider();
With the service provider available, we can go ahead and resolve dependencies directly from it:
var myService = serviceProvider.GetRequiredService<MyService>();
To wrap up this article, we don’t need to go with a hosted application if we want to leverage an IServiceCollection
in console applications for dependency injection. While the Microsoft Learn article is awesome with plenty of solid examples, I felt like it overcomplicated what could otherwise be a bit more straightforward.
So if you’re looking to go leverage an IServiceCollection
in your console projects instead of Autofac, go nuts! Even though I like using Autofac for all of my dependency injection, I think it’s important to understand the options that you have!
If you found this useful and you’re looking for more learning opportunities, consider subscribing to my free weekly software engineering newsletter and check out my free videos on YouTube!