Iniciador
Iniciar e inicializar la aplicación
Last updated
Iniciar e inicializar la aplicación
Last updated
El proceso de inicio y inicialización de la aplicación se conoce como bootstrapping. Esto puede llevar algún tiempo (de 2 a 10 segundos), dependiendo de la calidad del hardware y el número de módulos cargados. Cuando la aplicación se apaga, por lo general, la primera solicitud activa el inicio de la aplicación.
Durante el inicio de la aplicación, se llevan a cabo las siguientes acciones:
Se cargan todos los ensamblados principales en el dominio de la aplicación.
Se detectan y cargan todas las bibliotecas de módulos instalados en el dominio de la aplicación.
Se registran todos los servicios en el contenedor de servicios DI.
Se configura el pipeline(canalización) de solicitudes HTTP.
Se mapean los endpoints(puntosfinales) de ruta.
En una aplicación ASP.NET Core tradicional, estas acciones se realizan en Program.cs
(o Startup.cs
en versiones anteriores de ASP.NET), pero esta no es una opción para Crono porque los módulos externos necesitan engancharse/acoplarse en el proceso de inicio. Aquí es donde entran en juego los Iniciadores modulares.
El núcleo de la aplicación solo contiene un iniciador(bootstrapper) muy liviano (similar a un kernel). Después de que todas los ensamblados de módulos se cargan en el dominio de la aplicación, el escáner de tipos busca subclases concretas de la interfaz en todos los ensamblados. Los iniciadores se ordenan y se ejecutan uno tras otro.
Aquí está la definición de la interfaz IIniciador:
Además del método ConfigurarServicios
, la clase IniciadorBase
también proporciona el método ConfigurarContenedor
anulable. Hace lo mismo, pero al estilo Autofac
, usando ContainerBuilder
en lugar de IServiceCollection
. No importa si anulas ninguno, uno o ambos.
Para la ejecución condicional de los iniciadores, anula el método IniciadorBase.Coincidir()
y devuelve un valor que indique si se debe ejecutar o omitir el iniciador. Esto es útil cuando deseas permitir/suprimir la ejecución del iniciador según el estado de instalación de la aplicación.
Por defecto, los iniciadores se ejecutan en el orden en que fueron detectados por el escáner de tipos (primero los ensamblados principales, luego los ensamblados de módulos, y así sucesivamente). Esto se debe a que la clase IniciadorBase
asigna el valor predeterminado de la clase estática IniciadorOrdenacion
a la propiedad IIniciador.Orden
de forma predeterminada. Sin embargo, este valor se puede anular en la implementación de tu iniciador.
Si hay dos iniciadores con el mismo valor de Orden y es necesario especificar explícitamente el orden de ejecución, se utiliza el método IniciadorBase.EjecutarDespues(). Aquí tienes un ejemplo de implementación:
A veces, incluso el orden preciso de los iniciadores no es suficiente. Los middleware y los endpoints requieren un poco más de control, por ejemplo, si necesitas poder definir con precisión el orden de un componente de middleware dentro del pipeline(canalización) de solicitudes. Imagina que has desarrollado dos componentes de middleware en un solo módulo. Uno debe ser AntesStaticFilesMiddleware
y el otro DespuesRoutingMiddleware
. En Crono, puedes lograr esto de la siguiente manera:
Los inicializadores que implementan IInicializadorAplicacion
se utilizan para ejecutar código de inicialización de la aplicación durante la primera solicitud HTTP, y lo hacen muy temprano en el ciclo de vida de la solicitud. Esto los diferencia de los "iniciadores", que se ejecutan aún antes, cuando HttpContext
aún no está disponible.
Sin embargo, parte de la lógica de inicialización, como el acceso a HttpContext
, requiere un scope válido para resolver los servicios. No puedes acceder a dependencias con scope o transitorias dentro de un iniciador, a menos que crees manualmente un scope de dependencias, lo cual es una muy mala práctica y casi demoníaco 😄.
Por defecto, un inicializador solo se ejecuta una vez, a menos que especifiques un valor más alto en la propiedad IntentosMaximos
. Sin embargo, esta configuración no tiene efecto si la propiedad LanzarEnError
está configurada en true
. LanzarEnError
indica si se debe lanzar una excepción y detener la ejecución de los inicializadores subsiguientes en caso de error. Si su valor es false
, el inicializador se seguirá ejecutando y el método AlFallarAsync
será invocado para darte la oportunidad de registrar el error y corregirlo.
Inicializa la(s) base(s) de datos de la aplicación. Es el primer inicializador que se ejecuta.
Activa el programador web después de verificar nombres de host válidos. Retorna una advertencia si no hay un programador u organizaicón registrada.
Verifica nuevos registros de permisos y los inserta en la base de datos si es necesario.
Entre otras cosas, detecta y actualiza los recursos locales de los módulos que hayan cambiado.
En Crono, se utiliza la clase abstracta para mayor comodidad. Implementa la interfaz IIniciador
con métodos virtuales anulables, por lo que tu iniciador debe derivarse de esta clase en lugar de la interfaz IIniciador
.
La clase estática es muy útil aquí. Define numerosas constantes que representan el orden de los componentes de middleware conocidos (como StaticFiles, Routing, Authentication, ExceptionHandlers, ...). Solo necesitas enganchar antes o después de un componente.