Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 18 de octubre de 2022
ASP.NET Core

Normalmente, nuestras aplicaciones web ASP.NET Core son hosteadas por aplicaciones de consola, que son las encargadas de crearlas, configurarlas y lanzarlas. Esto suele hacerse mediante una relación de uno a uno: una única aplicación de consola se encarga de gestionar todo el ciclo de vida de una única aplicación web.

Pero, ¿es así necesariamente? En este post veremos que no.

Para entenderlo bien, echemos un vistazo al código de configuración y arranque que encontramos en el archivo Program.cs de una aplicación ASP.NET vacía en .NET 6:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

Estas nuevas APIs ponen bastante al descubierto la forma en que una aplicación es creada, configurada y lanzada: primero, usamos un builder para crear el objeto WebApplicationBuilder que configura sus aspectos básicos, luego definimos su comportamiento, y finalmente la ejecutamos. Esa última llamada a app.Run() bloquea la consola hasta que la aplicación web haya finalizado.

En este escenario, nuestro host (la aplicación de consola), es el encargado de realizar todos los preparativos previos, ejecutar la aplicación web y esperar a su finalización.

Sin embargo, si curioseamos un poco con intellisense, veremos que existe una versión asíncrona del método de ejecución, app.RunAsync(), que permite continuar la ejecución en paralelo a la puesta en marcha de la aplicación web. Esto quiere decir que en realidad el host podría continuar haciendo otras cosas mientras la aplicación se ejecuta... como por ejemplo, lanzar otras aplicaciones web 😉

El siguiente bloque de código muestra cómo lanzar dos aplicaciones web distintas sobre el mismo host, haciendo uso de esta posibilidad:

// Configuramos el sitio 1 (el frontal de una tienda)
var storeBuilder = WebApplication.CreateBuilder(args);
var store = storeBuilder.Build();
store.Urls.Add("https://localhost:5000");
store.MapGet("/", () => "Welcome to the store!");
store.MapGet(...); // Otros endpoints de la tienda online

// Configuramos el sitio 2 (la herramienta de administración)
var adminBuilder = WebApplication.CreateBuilder(args);
var admin = adminBuilder.Build();
admin.Urls.Add("https://localhost:5001");
admin.MapGet("/", () => "Welcome to the store administration!");
store.MapGet(...); // Otros endpoints de la tienda online

// Lanzamos y esperamos a que ambas acaben
await Task.WhenAll(store.RunAsync(), admin.RunAsync());

Al lanzarla, veremos que la consola recoge todas las trazas de inicialización de ambas aplicaciones y, si accedemos a los endpoints que cada una tiene configurados, podremos comprobar que se ejecutan sin problema:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Projects\TwoAppsDemo\
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Projects\TwoAppsDemo\

En fin, se trata de un truco con una probablemente limitada utilidad práctica, pero está bien saber que es posible por si acaso ;)

Publicado en Variable not found.

4 Comentarios:

Anónimo dijo...

He usado algo parecido, para crear una app que expone REST APIs y además tiene un un background job corriendo.

MontyCLT dijo...

Anónimo, ¿para un background job no es casi mejor utilizar un hosted service?

Y ya de paso, me gustaría preguntar, ¿alguien sabe si se puede utilizar Kestrel como un hosted service bajo un host genérico?

MontyCLT dijo...

He estado investigando un poco sobre eso, y al parecer el método ConfigureWebHost sobre el host genérico hace (junto con alguna configuración extra) lo siguiente:

builder.ConfigureServices((context, services) => services.AddHostedService());

Así que sí, Kestrel al parecer, no es más que simplemente un hosted service más.

José María Aguilar dijo...

@MontyCLT sí, en la práctica es así. La clase GenericWebHostService se registra como servicio hosteado y dentro se encuentra la instancia de Kestrel.

Saludos!