Hola! Aquí estamos de nuevo escribiendo sobre .Net. En esta oportunidad vamos a hablar de una funcionalidad que existe desde .Net Core 2.1, pero que sin embargo no es para nada conocida: los Startups múltiples.

Veamos el siguiente código. Corresponde a un Startup.cs de una aplicación Web API muy pequeña, que registra y/o configura servicios en función del ambiente.

public class Startup
{
    private readonly IWebHostEnvironment env;

    public Startup(IWebHostEnvironment env) =>
        this.env = env;

    public void ConfigureServices(
        IServiceCollection services)
    {
        if(env.IsDevelopment())
            services.AddDistributedMemoryCache();
        else
            services.AddDistributedRedisCache(options => { });
        
        // More services...
    }

    public void Configure(IApplicationBuilder app)
    {
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();

        // UseRouting, UseAuthorization, etc.
    }
}
  1. Si el ambiente es desarrollo, se configura un cache en memoria y se habilita la página de error.
  2. Si el ambiente NO es desarrollo, se utiliza como cache Redis y no se habilita la página de error.

Funcionar, funciona… Pero ese if validando el ambiente hace ruido. Además de odiar los ifs, mientras más crece la aplicación más crecen esas validaciones, y menos me gusta el código.

La funcionalidad oculta

Como mencionamos al comienzo del post, ya desde la versión 2.1 de .Net Core tenemos disponible una funcionalidad muy poco conocida llamada Startups Múltiples.

¿En qué consiste?

Si creamos una clase Startup con el nombre del ambiente como sufijo y lo registramos, el framework automáticamente ejecutará el Startup.cs que corresponda al ambiente.

Por ejemplo, podemos crear dos Startups, Startup.cs y StartupDevelopment.cs:

public class Startup
{
    public void ConfigureServices(
        IServiceCollection services)
    {
        services.AddDistributedRedisCache(options => { });
        // More services...
    }

    public void Configure(IApplicationBuilder app)
    {
        // UseRouting, UseAuthorization, etc.
    }
}
public class StartupDevelopment
{
    public void ConfigureServices(
        IServiceCollection services)
    {
        services.AddDistributedMemoryCache();
        // More services...
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseDeveloperExceptionPage();
        // UseRouting, UseAuthorization, etc.
    }
}

Finalmente, en la clase Program.cs dentro del método CreateHostBuilder sólo tenemos que registrar el emsamblado que contiene nuestros Startups.cs y sentarnos a disfrutar de las bondades de .Net.

public static IHostBuilder CreateHostBuilder(string[] args)
{
    var assemblyName = typeof(Startup).Assembly.FullName;

    return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup(assemblyName);
        });
}

En tiempo de ejecución se seleccionará el Startup cuyo sufijo corresponda con el nombre del ambiente de ejecución. Por ejemplo, si el ambiente es Development, se ejecutará la clase StartupDevelopment. Si no existe el startup correspondiente, se ejecutará la clase por defecto Startup.

¿Es esto siempre necesario?

Bueno, el ejemplo anterior es trivialmente simple como para justificar esta funcionalidad. Esta idea está pensada para aquellos casos donde el startup tiene muchos componentes que dependen del ambiente.

Sobre todo en desarrollo, donde es frecuente que mostremos errores con su stack, cambiemos los niveles de logging, deshabilitemos caches o los usemos en memoria, etc.

Conclusión

Acabamos de explorar una funcionalidad muy poco conocida dentro de .Net llamada Startups Multiples que nos permite tener distintas clases Startup.cs según el ambiente de ejecución. Esto nos permite tener clases más compactas y eliminar la necesidad de tener que validar constantemente el ambiente para inyectar o configurar servicios.