OdeToCode IC Logo

.NET Core Opinion 11 – Keep Configure Methods Clean Using Extension Methods

Monday, March 25, 2019

The Configure and ConfigureServices methods in ASP.NET Core tend to become bloated in larger projects. The problem is that larger projects can require lots of configuration, and the configuration requires lots of options. You open a project and there are dozens, even hundreds of lines of code, to configure OIDC, Swagger, authorization policies, data services, claims transformations, cookie options, CORS policies, domain services, and the list goes on and on.

I encourage team members to use extension methods so each line of code in the Configure methods is simple and the configuration details are hidden (yet easy to find by navigating with F12).

For example, instead of using lambdas and overwhelming the reader with lots of configuration details ...

public void ConfigureServices(IServiceCollection services)
{

    services.AddMvc(options =>
    {
        options.Conventions.Add(new FeatureControllerModelConvention());
    })
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Clear();
        options.ViewLocationFormats.Add(@"{3}\{0}.cshtml");
        options.ViewLocationFormats.Add(@"Features\Shared\{0}.cshtml");
        options.ViewLocationExpanders.Add(expander);
    });

    // and more code and more code and more code
}

... Provide a high-level overview of the app configuration, but keep the details encapsulated.

public void ConfigureServices(IServiceCollection services)
{
    services.AddCustomMvc();
    services.AddSecurity();
    services.AddCustomMediator();
    services.AddDataStores(Configuration.GetConnectionString(nameof(LeagueDb)));
}

Hide the configuration details in extension methods which are easy to find.

public static IServiceCollection AddCustomMvc(this IServiceCollection services)
{
    var expander = new FeatureViewLocationExpander();

    services.AddMvc(options =>
     {
         options.Conventions.Add(new FeatureControllerModelConvention());
     })
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Clear();
        options.ViewLocationFormats.Add(@"{3}\{0}.cshtml");
        options.ViewLocationFormats.Add(@"Features\Shared\{0}.cshtml");
        options.ViewLocationExpanders.Add(expander);
    });

    return services;
}

There’s also a good chance you can re-use some of the extension methods, particularly in a micro-services environment. However, the primary focus is to make the startup code usable and readable, not re-usable.


Comments
Gravatar Dmitry Pavlov Tuesday, March 26, 2019
It really depends on the case. I used to see some projects where configuration was so "clean" so I had to make 3-5 jumps through extension methods and other method/class "helpres" to see how exactly and what is configured. It's usually hard to make a decision by what criteria/principle to categorize the configuration pieces. So it is important to avoid cleaning which might result into a complete mess. :)
Gravatar Marcelo Tuesday, March 26, 2019
Hi Scot, this is the way I do it as well. Not only keeps the code cleaner and tidy but also helps giving intent to the code. I recenty developed an OAuth server based on OWIN that lives in an external class library. In it, I extend the IServicesCollection. As you know the amount of code to configure that beast is huge. It was so nice to apply this method and endup with: services.AddCustomAuthentication(); services.AddCustomAuthorization(); I know exactly what's goging on and my future self and other team members will be glad when they need to read this piece of code.
Gravatar Daniel P. Doyle Wednesday, March 27, 2019
I've recently moved to configuration classes, similar to automapper profiles or autofac modules. A Startup loader is executed from the startup class; profiles of service registrations, such as "AuthenticationProfile", "MvcProfile", etc, are loaded by assembly scanning against IServiceProfile interface. This splits what can be a huge Startup.cs into smaller chunks. Furthermore, new configuration profiles are picked up automatically, without changing the Startup.cs.
Gravatar Kirsten Kluge Monday, April 1, 2019
hi, if you are using typed settings in your project you can also benefit from this. I wrote a generic extension for this case. If you want you can have a look here: https://gist.github.com/kirkone/c1aa0dfeb9dcce1afbfd6c5508581da4 cu, KirK
Comments are closed.