usar seguidores populares permite para los hashtags funcionan cuantos conseguir como c# inversion-of-control unity-container ioc-container

c# - seguidores - ¿Cómo implementar IOC sin un servicio estático global(solución de localización que no sea de servicio)?



hashtag populares instagram 2018 (5)

+1 por saber que el Localizador de Servicios es una Cosa Mala .

El problema es que la unidad no es muy sofisticada, así que no sé qué tan fácil / difícil es hacer IoC de la manera correcta.

Escribí algunas entradas de blog recientemente que pueden ser útiles.

Queremos usar Unity para IOC. Todo lo que he visto es la implementación de que hay un servicio estático global (llamémoslo IOCService) que contiene una referencia al contenedor Unity, que registra todas las combinaciones de interfaz / clase y cada clase pregunta a ese objeto: dame una implementación por Ithis o IThat.

Con frecuencia veo una respuesta de que este patrón no es bueno porque lleva a una dependencia de TODAS las clases al IOCService (no al contenedor de Unity porque solo se conoce dentro del IOCService).

Pero lo que no veo a menudo es: ¿cuál es la forma alternativa?

Michel

EDITAR: descubrió que el servicio estático global se llama el localizador de servicios, que se agregó al título.


En lugar de usar el contenedor explícitamente, utilícelo implícitamente al aprovechar la inyección de constructor / propiedad en su lugar. Cree una clase principal (o un conjunto de clases principales) que dependa de todas las piezas principales de su aplicación.

La mayoría de los contenedores te permitirán poner ISomething[] en tu constructor e inyectarán todas las instancias de ISomething en tu clase.

De esta manera, cuando arranque su aplicación:

  1. Crea tu contenedor
  2. Registra todas tus golosinas
  3. Resuelva las clases principales (esto atraerá todas las demás dependencias que necesite)
  4. Ejecutar la parte "principal" de la aplicación.

Ahora, dependiendo del tipo de aplicación que está escribiendo, existen diferentes estrategias para evitar marcar el contenedor IoC como "estático".

Para las aplicaciones web ASP.NET, probablemente terminará almacenando el contenedor en el estado de la aplicación. Para las aplicaciones ASP.NET MVC, debe cambiar la fábrica de controladores.

Para las aplicaciones de escritorio, las cosas se complican más. Caliburn usa una solución interesante para este problema utilizando la construcción IResult (esto es para aplicaciones WPF, pero también podría adaptarse para formularios Windows Forms).


En teoría, para no tener que preocuparse por tener una instancia de IoC estática, debe seguir la Regla del Club de Lucha , es decir, no hablar sobre el club de lucha, es decir, no mencionar el contenedor IoC.

Esto significa que sus componentes no deberían estar enterados del contenedor IoC. Solo debe usarse en el nivel superior cuando se registran componentes. Si una clase necesita resolver algo, debería ser inyectada como una dependencia.

El caso trivial es bastante fácil. Si PaymentService depende de IAccount , IoC debe inyectar este último:

interface IAccount { Deposit(int amount); } interface CreditCardAccount : IAccount { void Deposit(int amount) {/*implementation*/} int CheckBalance() {/*implementation*/} } class PaymentService { IAccount account; public PaymentService (IAccount account) { this.account = account; } public void ProcessPayment() { account.Deposit(5); } } //Registration looks something like this container.RegisterType<IAccount, CreditCardAccount>(); container.RegisterType<PaymentService>();

El caso no tan trivial es donde desea inyectar registros múltiples. Esto se aplica especialmente cuando está realizando cualquier tipo de Converstion Over Configuration y creando un objeto a partir de un nombre.

Para nuestro ejemplo de pago, digamos que desea enumerar a través de todas las cuentas y consultar sus saldos:

class PaymentService { IEnumerable<IAccount> accounts; public PaymentService (IEnumerable<IAccount> accounts) { this.accounts = accounts; } public void ProcessPayment() { foreach(var account in accounts) { account.Chackbalance(); } } }

Unity tiene la capacidad de registrar múltiples interfaces para asignaciones de clase (tienen que tener nombres diferentes). Sin embargo, no los inyecta automáticamente en clases que toman colecciones de esas interfaces registradas. Por lo tanto, el ejemplo anterior arrojará una excepción de resolución fallida en el tiempo de ejecución.

Si no le importa que esos objetos vivan para siempre, puede registrar PaymentService de una manera más estática:

container.RegisterType<PaymentService>(new InjectionConstructor(container.ResolveAll<IAccount>()));

El código anterior registrará PaymentService y utilizará una colección de instancias de IAccount que se resuelve en el momento del registro.

Alternativamente , puede pasar una instancia del contenedor como una dependencia y dejar que PaymentService realice la resolución de las cuentas. Esto no sigue la Regla del Club de Lucha, pero es un poco menos mal que el Localizador de Servicios estático.

class PaymentService { IEnumerable<IAccount> accounts; public PaymentService (IUnityContainer container) { this.accounts = container.ResolveAll<IAccount>(); } public void ProcessPayment() { foreach(var account in accounts) { account.Chackbalance(); } } } //Registration is pretty clean in this case container.RegisterType<IAccount, CreditCardAccount>(); container.RegisterType<PaymentService>(); container.RegisterInstance<IUnityContainer>(container);


La alternativa es tener una sola instancia de su contenedor solo en el nivel más alto de la aplicación , luego usar ese contenedor para resolver cada instancia de objeto que necesite crear en esa capa.

Por ejemplo, el método principal de la mayoría de los ejecutables se ve así (sin el manejo de excepciones):

private static void main(string[] args) { Container container = new Container(); // Configure the container - by hand or via file IProgramLogic logic = container.Resolve<IProgramLogic>(); logic.Run(); }

Su programa (representado aquí por la instancia de IProgramLogic ) no necesita saber nada sobre su contenedor, porque container.Resolve creará todas sus dependencias, y las dependencias de sus dependencias, hasta las clases de hoja sin dependencias propias.

ASP.NET es un caso más difícil, porque los formularios web no admiten la inyección de constructores. Normalmente uso Model-View-Presenter en mis aplicaciones de formularios web, por lo que mis clases de Page solo tienen una dependencia cada una: su presentador. No los hago pruebas de unidad (todo lo que es interesante y comprobable está en mis presentadores, que hago pruebas), y nunca sustituyo a los presentadores. Así que no peleo con el marco, solo HttpApplication una propiedad de contenedor en mi clase HttpApplication (en global.asax.cs) y la uso directamente desde los archivos de mi Page :

protected void Page_Load(object sender, EventArgs args) { ICustomerPresenter presenter = Global.Container.Resolve<ICustomerPresenter>(); presenter.Load(); }

Por supuesto, ese es el localizador de servicios, aunque las clases de la Page son lo único que se acopla al localizador: su presentador y todas sus dependencias aún están completamente desconectadas de la implementación del contenedor IoC.

Si tiene muchas dependencias en sus archivos de Page (es decir, si no usa Model-View-Presenter), o si es importante que desacople sus clases de Page de su clase de aplicación Global , debe intentar encontrar un el marco que se integra en los formularios web solicita la canalización y utiliza la inyección de propiedades (como lo sugiere Nicholas en los comentarios a continuación), o escriba su propio IHttpModule y realice la inyección de propiedades usted mismo.


Si su inquietud es tener una dependencia de Unity en toda su aplicación, puede combinar el localizador de servicios con una fachada para ocultar la implementación de IOC. De esta forma, no crea una dependencia de Unity en su aplicación, solo tiene algo que puede resolver los tipos por usted.

Por ejemplo:

public interface IContainer { void Register<TAbstraction,TImplementation>(); void RegisterThis<T>(T instance); T Get<T>(); } public static class Container { static readonly IContainer container; public static InitializeWith(IContainer containerImplementation) { container = containerImplementation; } public static void Register<TAbstraction, TImplementation>() { container.Register<TAbstraction, TImplementation>(); } public static void RegisterThis<T>(T instance) { container.RegisterThis<T>(instance); } public static T Get<T>() { return container.Get<T>(); } }

Ahora todo lo que necesita es una implementación de IContainer para el contenedor de su elección de IOC.

public class UnityContainerImplementation : IContainer { IUnityContainer container; public UnityContainerImplementation(IUnityContainer container) { this.container = container; } public void Register<TAbstraction, TImplementation>() { container.Register<TAbstraction, TImplementation>(); } public void RegisterThis<T>(T instance) { container.RegisterInstance<T>(instance); } public T Get<T>() { return container.Resolve<T>(); } }

Ahora tiene un localizador de servicios que es una fachada para los servicios de IOC, y puede configurar su localizador de servicios para usar Unity o cualquier otro contenedor de IOC. El resto de la aplicación no depende de la implementación de IOC.

Para configurar su localizador de servicio:

IUnityContainer unityContainer = new UnityContainer(); UnityContainerImplementation containerImpl = new UnityContainerImplementation(unityContainer); Container.InitializeWith(containerImpl);

Para las pruebas, puedes crear un código auxiliar de IContainer que devuelva lo que quieras e inicializar Container con eso.