Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why AddMvc expects Action<MvcOptions> instead of MvcOptions?

I'm learning ASP.NET Core and I see that registering an MVC service looks like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.MaxModelValidationErrors = 100;
    });
}

My question is, why does the AddMvc method require options in the form of Action<MvcOptions>? Why can't I just create an instance of MvcOptions and pass it to the function?

like image 706
mnj Avatar asked Jan 11 '19 13:01

mnj


1 Answers

If you look at the source for AddMvc, you'll see that it doesn't create an instance of MvcOptions for you:

public static IMvcBuilder AddMvc(this IServiceCollection services, Action<MvcOptions> setupAction)
{
    ...

    var builder = services.AddMvc();
    builder.Services.Configure(setupAction);

    return builder;
}

Instead, it uses IServiceCollection.Configure to hook into the more general Options pattern in ASP.NET Core. Behind the scenes, this adds an instance of IConfigureOptions<MvcOptions> to the Dependency Injection container, which will end up running your delegate at some point later in time.

It's possible to add multiple instances of IConfigureOptions<MvcOptions>, which will be run in order of registration. There's also IServiceCollection.PostConfigure, which registers instances of IPostConfigureOptions<MvcOptions> - these instances will run after all of the IConfigureOptions<MvcOptions> instances (docs).

This all offers some flexibility. You can set up a pipeline of delegates for configuring MvcOptions in a set order, where each configuration step might come from other projects, etc. You could even have your own call to services.Configure<MvcOptions>(...) before your call to AddMvc, etc.


When adding the MVC services to DI, it's possible to use either AddMvc or AddMvcCore. Internally, AddMvc calls AddMvcCore, so we can think of AddMvc as some kind of extension of AddMvcCore.

AddMvcCore adds its own configuration, rather than creating an instance of MvcOptions itself. It adds a set of IConfigureOptions<MvcOptions> and IPostConfigureOptions<MvcOptions> to the Dependency Injection container.

These two interfaces are used to assemble a form of pipeline, where all IConfigureOptions<MvcOptions> run first (in the order they're added to DI) and all IPostConfigureOptions<MvcOptions> run second (again, in order). This allows AddMvcCore to provide some defaults (using IConfigureOptions<MvcOptions>) and also provides the ability to make changes to MvcOptions once all other configurations have been applied (using IPostConfigureOptions<MvcOptions>).

When you call AddMvc and provide a delegate, said delegate will run after the IConfigureOptions<MvcOptions> added by AddMvcCore, which provides the ability to override these defaults within your application.

like image 144
Kirk Larkin Avatar answered Sep 29 '22 00:09

Kirk Larkin