The ServiceCollection Extension Pattern

One of the first things people notice when making the jump from the full .NET Framework to .NET Core is the inbuilt dependency injection. Having a DI framework right there ready to be used means less time trying to set up a boilerplate project.

Interestingly, a pattern that emerged from this was one whereby an extension of the service collection was used to add services. For example, you might see something like this :

public void ConfigureServices(IServiceCollection services)
{
	services.AddMvc();
}

But what is actually inside the “AddMvc” call? Jumping into the source code (And going a few more levels deeper) it actually looks like this :

....
services.TryAddSingleton<MvcMarkerService, MvcMarkerService>();
services.TryAddSingleton<ITypeActivatorCache, TypeActivatorCache>();
services.TryAddSingleton<IUrlHelperFactory, UrlHelperFactory>();
...

So under the hood it’s just doing plain old additions to the service collection, but it’s moved away into a nice tidy “add” call. The pattern might have been around for a while but it was certainly the first time I had seen it. And it’s certainly easy on the eye when looking into your startup.cs. Instead of 100’s of lines of adding services (Or private method calls), there is just a couple of extensions being used.

Building Your Own Extension

Building your own extension is simple. A glimpse into the project structure I’ll use for this demo is here :

All pretty standard. I have a Services folder with MyService and it’s interface inside it. Inside there we have a file called “ServicesConfiguration”. The contents of that file looks like the following :

public static class ServicesConfiguration
{
	public static void AddCustomServices(this IServiceCollection services)
	{
		services.AddTransient<IMyService, MyService>();
	}
}

So this has a single method that is marked as static (important!). The method accepts a param of IServiceCollection as “this”, also very important. Inside here, we simply bind variables how we normally would any other way.

Now if we go to our startup.cs, We can add a call to this method like so :

public void ConfigureServices(IServiceCollection services)
{
	services.AddMvc();
	services.AddCustomServices();
}

Obviously as we add more services, our startup.cs doesn’t change it stays nice and clean. We simply add more to our ServicesConfiguration.

…But Is It A Good Pattern?

There are a few caveats I feel when using this pattern. And both are to do with building libraries or sharing code.

You are locked into the ServiceCollection DI

This isn’t necessarily true since another developer could simply choose not to call the “AddXYZServices” method, but it’s likely going to cause a lack of documentation around how things work. I would guess that most documentation would boil down to “Now just add services.AddXYZServices() and you are done” rather than actually go into detail about the interfaces and classes that are required.

7 thoughts on “The ServiceCollection Extension Pattern”

  1. Could you please post full examples so we can see the required using statements etc? Hard to implement this only from fragments. Thanks.

    Reply
  2. I like this pattern very much. It allows me to add the configuration of a feature into my application layer instead of the UI layer/project. I like to have all application logik in a different DLL to make it reusable. It allows me to do so and keep the feature and its configuration together.

    Because I use the Add[Service] methods only in a class Startup and an instance of IConfiguration is already there I am good with passing in the instance as a parameter.

    https://docs.microsoft.com/en-us/dotnet/core/extensions/options-library-authors#:~:text=The%20Configure%3CTOptions%3E%20%28IServiceCollection%2C%20IConfiguration%29%20method%20is%20part%20of,instance%20of%20IServiceCollection%20Defines%20an%20IConfiguration%20parameter%20namedConfigurationSection shows some ways to configure a service “the right way”.

    Reply
  3. “it’s likely going to cause a lack of documentation around how things work. I would guess that most documentation would boil down to “Now just add services.AddXYZServices() and you are done” rather than actually go into detail about the interfaces and classes that are required.”

    !! This is soo true.

    Reply

Leave a Comment