Buenas prácticas de DI
Crono usa Autofac para la inyección de dependencias, pero los módulos normalmente registran sus servicios en una clase de inicio derivada de IniciadorBase. Dentro de ConfigurarServicios puedes acceder al IServiceCollection de ASP.NET Core y al IAplicacionContexto; para registros que solo permite Autofac (fuentes de registro, decoradores, metadatos, Lazy<>, etc.) usa ConfigurarContenedor con el ContainerBuilder (ver Inyección de dependencias e Iniciador).
internal class Startup : IniciadorBase
{
public override void ConfigurarServicios(IServiceCollection servicios, IAplicacionContexto appContexto)
{
if (appContexto.EstaInstalado)
{
servicios.AddScoped<MiServicio>();
}
// Sobrescribir un servicio existente: gana el último registro
servicios.AddScoped<IPrecioServicio, PrecioServicioPersonalizado>();
}
}
IComunServicios
IComunServicios agrupa los ayudantes de uso más frecuente: DbContexto (CronoDbContexto), TrabajoContexto, OrganizacionContexto, Cache (ICacheAdministrador), CacheSolicitud, EventoPublicador, Localizacion, Configuraciones, RegistradorActividad, entre otros.
En los controladores y componentes de vista que derivan de CronoController se inyecta por propiedad y está disponible en el miembro Servicios, sin parámetros de constructor:
public class EjemploController : CronoController
{
public IActionResult Index()
{
var usuario = Servicios.TrabajoContexto.UsuarioActual;
return View();
}
}
Inyéctalo manualmente solo en otras clases que necesiten muchos de sus ayudantes, para mantener las dependencias explícitas. Para controladores es cómodo, pero abusar de él en servicios dificulta las pruebas unitarias.
Resolver fuera del pipeline
Para obtener o crear un ámbito fuera del pipeline de la solicitud (trabajos en segundo plano, herramientas de línea de comandos), resuelve desde un ILifetimeScope en lugar del contenedor raíz, para asegurar que las instancias se liberen correctamente. En las tareas programadas, el TareaEjecucionContexto ya ofrece Resolver<T>() / ResolverPorNombre<T>() para resolver dependencias dentro del ámbito de la ejecución (ver Programación de tareas).
Nunca resuelvas dependencias con ámbito desde IAplicacionContexto.Servicios (el contenedor raíz): solo las dependencias singleton deben resolverse desde ahí. Resolver dependencias con ámbito desde la raíz puede provocar fugas de memoria.
Trabajo<T>
Trabajo<T> resuelve un servicio de forma diferida desde el ámbito actual. Es útil cuando un servicio se necesita solo ocasionalmente o dentro de bucles, evitando construcciones innecesarias:
public class Ejemplo
{
private readonly Trabajo<IMiServicio> _trabajo;
public Ejemplo(Trabajo<IMiServicio> trabajo) => _trabajo = trabajo;
public void Hacer() => _trabajo.Valor.Ejecutar();
}
Siguiendo estas pautas, los módulos se mantienen ligeros y los tiempos de vida de las dependencias, predecibles.