Reducing Boilerplate on .NET IHost Applications

In this post, you’ll find how to reduce boilerplate on .NET IHost applications, enhance development speed, code readability and maintenance.

Hey, listen… Suppose you have a huge .NET solution with a bunch of Swagger projects or even pure MVC applications. If you look closer, you’ll realize that most of the code you wrote to initialize the applications is the same, it is known as boilerplate. Boilerplate code, while necessary, can lead to increased development time, reduced code maintainability, and potential errors. Fortunately, there are techniques to reduce boilerplate. In this post, I will show how to reduce boilerplate on .NET IHost applications, enhancing developer productivity and code quality.

This post will focus on dependency injection and extension methods however there are plenty of ways to reduce the boilerplate such as T4 templates or tools like AutoFixture (for testing), which may be explored in future posts.

Dependency Injection (DI)

Dependency Injection is a fundamental concept in modern software development and plays a crucial role in reducing boilerplate. By leveraging DI, you can inject services and components where needed, eliminating the need for explicit instantiation and reducing repetitive code. The .NET Core framework has a built-in DI container that makes this process easy.

The next code shows a service class and how to use it on a controller.

You must register MyDearService, at Startup.cs or Program.cs, with services.AddScoped<MyDearService>() for the class to be available to be used in the controller.

public class MyDearService
{
    // Service implementation
    public async Task DoSomethingSpecial() {
        // special stuff here
    }
}

public class MyDearController : ControllerBase
{
    private readonly MyDearService _myDearService;

    public MyController(MyDearService myService)
    {
        _myDearService = myService;
    }

    [HttpGet]
    public async Task<IActionResponse> MyNiceMethod() {
        // Controller logic using MyDearService
        await _myDearService.DoSomethingSpecial();
    }
}

Extension Methods

To keep the Startup process clean and readable, encapsulate service registrations in extension methods. This not only reduces code size in the Startup process but also enhances readability and organization.

Example:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddBootstrapServices(this IServiceCollection services)
    {
        services.AddScoped<MyDearService>();
        // You can add many services as you need
        return services;
    }
}

// Then you can call it in Startup.cs (if ufing .NET prior to v6)
public void ConfigureServices(IServiceCollection services)
{
    services.AddBootstrapServices();
    // Other configurations
}

// Or call it in the Program.cs (if ufing .NET after v6)
services.AddBootstrapServices();

Practical Example

As proposed at the beginning of this post, imagine a solution with many projects, each one being a swagger API. Now instead of configuring each one individually, let’s use a base project called Commons. The Commons project will contain all the jazz necessary to configure and boot the application.

Solution with two swagger APIs and a shared Commons project
Solution with two swagger APIs and a shared Commons project

The Commons project has a Boot class, with the needed code to run the application and auxiliary extension classes with methods to register services and configure the pipeline.

Of course, you’ll need to install the Swashbuckle package to use swagger-related methods such as AddSwaggerGen and UseSwaggerUI.

To install the package use the Visual Studio Nuget manager or run the following command in the Commons project folder:

dotnet add package Swashbuckle.AspNetCore

ServiceExtensions class

This extension class holds the code to register all the necessary services. As this is a simple example, I’m only registering the swagger-related services but it could also have some other services like database and remote services like complementary REST services.

public static class ServiceExtensions
{
    public static IServiceCollection RegisterSwaggerServices(this IServiceCollection services)
    {
        // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
        services.AddEndpointsApiExplorer();
        services.AddSwaggerGen();

        return services;
    }
}

WebApplicationExtensions class

Here we should create all the methods needed to configure the pipeline. Again, I’m only configuring Swagger but one could add methods to configure and extend the pipeline to read request headers for instance.

public static class WebApplicationExtensions
{
    public static WebApplication ConfigureSwaggerPipeline(this WebApplication webApp)
    {
        // Configure the HTTP request pipeline.
        if (webApp.Environment.IsDevelopment())
        {
            webApp.UseSwagger();
            webApp.UseSwaggerUI();
        }
        return webApp;
    }
}

Boot class

This class has a single method to run the main application and call the extension methods builder.Services.RegisterSwaggerServices() and app.ConfigureSwaggerPipeline()

public static class Boot
{
    public static void Run(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        
        // Add services to the container.

        builder.Services.AddControllers();
        builder.Services.RegisterSwaggerServices();

        var app = builder.Build();

        app.ConfigureSwaggerPipeline();

        app.UseHttpsRedirection();
        app.UseAuthorization();
        app.MapControllers();
        app.Run();
    }
}

Program class

To run the SimpleApi1 and SimpleApi2 programs we have to write a single line of code and call the static method Run from the Boot class. Now we can see great improvement in speed and code reuse.

Boot.Run(args);

The programmer has to make changes only in the Commons project when a new service is needed by the APIs or when some adjustments must be made.

Conclusion

In the ever-evolving landscape of .NET development, reducing boilerplate code is crucial for clean, scalable and maintainable applications. Dependency Injection and simple Extension Methods are effective strategies to streamline .NET IHosted applications. By implementing these simple techniques, developers can focus more on business logic and innovation, improving the overall code quality and development speed.

Get the source: https://github.com/raffsalvetti/ReduceBoilerplate

See you!