.NET Core Tutorial: Using the ServiceCollection Extension Pattern

If you have worked on applications (in .NET Core) you should know how Dependency Injection (DI) is supported and is applied for your services. This is straight forward and not so hard at all. Just add them to the IServiceCollection in the ConfigureServices() pipeline in the startup.cs file. Considering a N-Tier architrecture, where our Startup.cs is in the Project.Host project and mall example:

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IService, Service>();
            services.AddScoped<IProcessor, Processor>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }

Now, in the code snippet above, we just add a service, a processor and the IHttpContextAccessor to the DI container. As the application grows and grows, more and more services will be added here and on a long term, this will just bloat up the startup.cs file.

For example, there is a new project added to the solution Project.Reporting which hold services like IPdfReportGeneratorService and a IPdfReportConverterService, and maybe a few more for demonstration purposes. What you would normally do is just add a reference to the reporting project and just add those services to the container. You can see already how this is adding up to the pipeline.

using Solution.Project.Reporting.Services;

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IPdfReportGeneratorService , PdfReportGeneratorService >();
            services.AddTransient<IPdfReportConverterService, PdfReportConverterService>();
            services.AddTransient<IServiceThis, ServiceThis>();
            services.AddTransient<IServiceThat, ServiceThat>();
            services.AddTransient<IServiceOther, ServiceOther>();

            services.AddTransient<IService, Service>();
            services.AddScoped<IProcessor, Processor>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }

We can clean this up a bit. The Service Collection Extension Pattern is exactly what it says. We add our services in a extension method in its own project. The goal is to keep this pipeline as clear as possible. Let’s do this for the Project.Reporting library in the aplication.

Inside the reporting project, we add a new static class called ReportingServiceCollectionExtensions at the root of the project. Inside we create a extension method where we extend the IServiceCollection and add the services. We also return the services – because the service registration implements the Chain Pattern.

using Microsoft.Extensions.DependencyInjection;

public static class ReportingServiceCollectionExtensions
{
    public static IServiceCollection AddReporting(this IServiceCollection services)
    {
        services.AddTransient<IPdfReportGeneratorService , PdfReportGeneratorService >();
            services.AddTransient<IPdfReportConverterService, PdfReportConverterService>();
            services.AddTransient<IServiceThis, ServiceThis>();
            services.AddTransient<IServiceThat, ServiceThat>();
            services.AddTransient<IServiceOther, ServiceOther>();

        return services;
    }
}

Now we can easily call this extension in our RegisterServices pipeline in the startup.cs file in the host project by calling services.AddReporting();. If you do this for each project/assembly/layer in your solution, this keeps the startup file nice and clean.

using Solution.Project.Reporting.Services;

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddReporting();

            services.AddTransient<IService, Service>();
            services.AddScoped<IProcessor, Processor>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }

I hope you understand what the Service Collection Extension Pattern is now and how to implement it. If you have any feedback or questions, do not hesitate to comment.

T4Executer VS2019 Extension

When working with T4 templates in Visual Studio, you have to manually execute the custom for it to trigger the code generation. I used AutoT4 in the past (a VS2012 extension) to execute template on build time but this extension is not supported in the latest version(s) of Visual Studio.

T4Executer is open for contributions and improvements, the source code can be found here. To use, just install the extension. You can download it at the marketplace.

It is a pretty simple package, that ties execution of specified T4 templates to BuildEvents (BuildEvents interface). It’s possible to configure which templates to run before build, after build or just ignore a template completely.

Usage

You can configure T4Executer viaExtensions - T4Executer - Configure. A window will open that lists all templates found in your solutions’ projects.

Click on the template you want and specify to run before or after build by clicking the button under the list. You can also select multiple templates at a once. Templates you move to ignore will never be run, this can be useful for .tt files which don’t generate code or just hold some Class Feature Blocks or a collection of Import Directives. Note that the extension filters out .ttinclude files as this extension is mainly used for files that do not output generated code.

By default all templates are executed before your projects build event, when building or rebuilding your solution. Selecting Extensions - T4Executer - Disable will disable this default behaviour.

Timestamps

The timespamp of the generated the generated file is by default not preserved when the content of the file is not changed. There is a option to toggle this via Extensions - T4Executer - Preserve Timestamps.

I hope this helps you while working with T4 Templates in your solution. If you have any questions or feedback, please leave a comment below.