ventajas usar porque net español entorno desarrollo con campusmvp asp aprender c# autofac

c# - net - porque usar docker



Autofac: resolver los parámetros de tiempo de ejecución sin tener que pasar el contenedor (3)

Tengo una clase más simple "ServiceHelper" que toma dos parámetros en el constructor:

public ServiceHelper(ILogger<ServiceHelper> log, string serviceName)

(El contenedor genérico de ILogger para NLog que Autofac proporciona muy bien, y serviceName es el nombre de un servicio de Windows para controlar que debo proporcionar en tiempo de ejecución).

Estoy teniendo problemas para comprender cómo crear nuevas instancias de esta clase en tiempo de ejecución al pasar a diferentes nombres de servicio, usando Autofac. Por supuesto, algo como esto no funciona, ya que necesito especificar diferentes nombres de servicio en tiempo de ejecución:

builder.RegisterType<ServiceHelper>().As<IServiceHelper>().WithParameter(new NamedParameter("serviceName", null)).InstancePerDependency();

Por lo que he leído, es un mal hábito pasar el contenedor y hacer clic en Resolver manualmente (el "antipatrón" del Localizador de Servicios advierte sobre el AutoFac), ¿o no? Si hiciera eso entonces podría hacer

container.Resolve<ServiceHelper>(new NamedParameter("serviceName", "some service name"));

Pero para llegar tan lejos, no estoy muy seguro de cómo hacer que Autofac inyecte el contenedor en las clases, solo tendría que registrarse por sí mismo. ¿Y luego mis clases requieren un IContainer en sus constructores? (Esto es en un servicio de C # usando inyección de constructor)

builder.RegisterType<Container>().As<IContainer>().InstancePerDependency();

También leí sobre fábricas delegadas, pero eso no parece alejarse de tener que pasar el contenedor.

Realmente, la mayoría de mis clases que consumen ServiceHelper, solo necesitan 1 o 2 ServiceHelpers para nombres de servicio específicos, así que no es como si estuviera haciendo miles con parámetros inesperados de serviceName, esto solo hace que me duela un poco la cabeza.


La resolución debería ocurrir solo para los objetos de composición raíz. La resolución de llamadas es casi lo mismo que "renovar" un objeto, que es una prueba de olor. Hay momentos en que la resolución es dinámica y solo se puede determinar sobre la marcha, pero la mayoría de las dependencias son deterministas y se pueden registrar por adelantado. Cómo hacer esto con Autofac es el desafío. La respuesta otorgada por @Christian Specht es una buena respuesta, pero asume que todo está determinado en el tiempo de ejecución.

Para definir una cadena de dependencias en el momento del diseño, vea el tema SO registro de cadenas de dependencias de Autofac ...


Sí, pasar el contenedor por todas partes es un anti-patrón.

Puedes evitarlo usando una fábrica como esta:

(nota: todo el código en esta respuesta no se ha probado, lo escribo en un editor de texto en una máquina sin Visual Studio)

public interface IServiceHelperFactory { IServiceHelper CreateServiceHelper(string serviceName); } public class ServiceHelperFactory : IServiceHelperFactory { private IContainer container; public ServiceHelperFactory(IContainer container) { this.container = container; } public IServiceHelper CreateServiceHelper(string serviceName) { return container.Resolve<ServiceHelper>(new NamedParameter("serviceName", serviceName)); } }

En el inicio, registra ServiceHelperFactory en Autofac, como todo lo demás:

builder.RegisterType<ServiceHelperFactory>().As<IServiceHelperFactory>();

Luego, cuando necesite un ServiceHelper en otro lugar, puede obtener la fábrica a través de una inyección de constructor:

public class SomeClass : ISomeClass { private IServiceHelperFactory factory; public SomeClass(IServiceHelperFactory factory) { this.factory = factory; } public void ThisMethodCreatesTheServiceHelper() { var helper = this.factory.CreateServiceHelper("some service name"); } }

Al crear la propia fábrica mediante la inyección del constructor con Autofac, se asegura de que la fábrica sepa sobre el contenedor, sin tener que pasar el contenedor por su cuenta.

Admito que, a primera vista, esta solución no parece muy diferente a pasar el contenedor directamente. Pero la ventaja es que su aplicación aún está desacoplada del contenedor, el único lugar donde se conoce el contenedor (excepto el inicio) es dentro de la fábrica.

EDITAR:

Ok lo olvidé Como dije anteriormente, estoy escribiendo esto en una máquina sin Visual Studio, así que no puedo probar mi código de ejemplo.
Ahora que leí su comentario, recuerdo que tuve un problema similar cuando usé Autofac e intenté registrar el contenedor.

Mi problema era que necesitaba registrar el contenedor en el constructor.
Pero para que la instancia del contenedor se registrara, necesitaba llamar al builder.Build() ... que crea el contenedor, lo que significa que no puedo registrar cosas en el builder después.
No recuerdo el mensaje de error que recibí, pero supongo que ahora tienes el mismo problema.

La solución que encontré fue crear un segundo generador, registrar el contenedor allí y luego usar el segundo generador para actualizar el único y único contenedor .

Aquí está mi código de trabajo de uno de mis proyectos de código abierto:

Al inicio, registro el contenedor :

var builder = new ContainerBuilder(); // register stuff here var container = builder.Build(); // register the container var builder2 = new ContainerBuilder(); builder2.RegisterInstance<IContainer>(container); builder2.Update(container);

... que luego utiliza WindowService para crear nuevas ventanas de WPF :

public class WindowService : IWindowService { private readonly IContainer container; public WindowService(IContainer container) { this.container = container; } public T GetWindow<T>() where T : MetroWindow { return (T)this.container.Resolve<T>(); } }


Seguí el camino del enfoque anterior y funciona bien, sin embargo, me resultó imposible realizar una prueba unitaria debido a que el método "Resolver <>" en IContainer es un método de extensión. También nunca se sintió realmente "bien" con toda la charla sobre no pasar su contenedor.

Regresé al tablero de dibujo y encontré la forma "correcta" de crear una instancia de los objetos utilizando Autofac Delegate Factories http://docs.autofac.org/en/latest/advanced/delegate-factories.html