ioc injection dependency control and wpf design-patterns dependency-injection inversion-of-control autofac

injection - ¿Es posible la inyección de dependencias con una aplicación WPF?



dependency injection or ioc inversion of control (9)

Creo que has llegado al tema. Los controles deben inyectarse en sus padres en lugar de crearse declarativamente a través de XAML.

Para que DI funcione, un contenedor DI debe crear la clase que está aceptando dependencias. Esto significa que el padre no tendrá ninguna instancia de los controles secundarios en el momento del diseño y se verá como un shell en el diseñador. Este es probablemente el enfoque recomendado.

La otra "alternativa" es tener un contenedor estático global llamado desde el constructor del control, o algo similar. Hay un patrón común en el que se declaran dos constructores, uno con una lista de parámetros para la inyección del constructor y el otro sin parámetros que delega:

// For WPF public Foo() : this(Global.Container.Resolve<IBar>()) {} // For the rest of the world public Foo(IBar bar) { .. }

Casi llamaría a esto un antipatrón, pero por el hecho de que algunos marcos no dejan otra opción.

Ni siquiera soy medio experto en WPF, así que espero una porción saludable de downmod aquí :) pero espero que esto ayude. El grupo Autofac (vinculado desde la página de inicio) podría ser otro lugar para hacer esta pregunta. Las aplicaciones de muestra Prism o MEF (que incluyen algunos ejemplos de WPF) deberían darle una idea de lo que es posible.

Quiero comenzar a usar la inyección de dependencia en mi aplicación WPF, en gran medida para una mejor capacidad de prueba de la unidad. Mi aplicación se basa principalmente en el patrón MV-VM. Estoy buscando autofac para mi contenedor IoC, pero no creo que eso sea demasiado importante para esta discusión.

Inyectar un servicio en la ventana de inicio parece sencillo, ya que puedo crear el contenedor y resolverlo en App.xaml.cs.

¿Con qué estoy luchando es cómo puedo ver los modelos y servicios de DI en los controles de usuario? Los controles de usuario se instancian mediante el marcado XAML, por lo que no hay oportunidad de Resolve() .

Lo mejor que puedo pensar es colocar el contenedor en un Singleton y hacer que los controles del usuario resuelvan sus ViewModels del contenedor global. Esto se siente como una solución a mitad de camino, en el mejor de los casos, ya que todavía requería que mis componentes tuvieran una dependencia en un ServiceLocator.

¿Es posible un IoC completo con WPF?

[edit] - Se ha sugerido Prism, pero incluso la evaluación de Prism parece una gran inversión, estoy esperando algo más pequeño

[edit] aquí hay un fragmento de código donde estoy parado

//setup IoC container (in app.xaml.cs) var builder = new ContainerBuilder(); builder.Register<NewsSource>().As<INewsSource>(); builder.Register<AViewModel>().FactoryScoped(); var container = builder.Build(); // in user control ctor - // this doesn''t work, where do I get the container from VM = container.Resolve<AViewModel>(); // in app.xaml.cs // this compiles, but I can''t use this uc, //as the one I want in created via xaml in the primary window SomeUserControl uc = new SomeUserControl(); uc.VM = container.Resolve<AViewModel>();


Creo que tienes que decidir primero en View First o Viewmodel First, ya que, dada la otra respuesta, se puede decidir ... Hay varios framework de código abierto que lo hacen. Uso Caliburn donde primero se toma ViewModel y su enfoque realmente bueno


Deberías echarle un vistazo a Caliburn : es un marco simple de WPF / Silverlight MVC con soporte para DI completo. Se ve muy bien y te permite usar cualquier contenedor IoC que desees. Hay un par de ejemplos en la wiki de documentación


Deberías echarle un vistazo a Prism del equipo de p & p. Aquí está el sitio en Codeplex


En realidad es muy fácil de hacer. Tenemos ejemplos de esto en Prism como mencionó jedidja. Puede inyectar ViewModel con la Vista o inyectar View con ViewModel. En Prism StockTraderRI verá que inyectamos View en ViewModel. Esencialmente, lo que sucede es que la vista (y la interfaz de visualización) tiene una propiedad de modelo. Esa propiedad se implementa en el código subyacente para establecer DataContext en el valor, por ejemplo: this.DataContext = value; . En el constructor de ViewModel, la Vista se inyecta. Luego establece View.Model = this; que se pasará como el DataContext.

También puede hacer lo contrario fácilmente y hacer que el ViewModel se inyecte en la Vista. En realidad, prefiero esto porque significa que ViewModel ya no tiene ninguna referencia posterior a la vista. Esto significa que cuando se prueba la unidad del ViewModel no se tiene siquiera una vista para simular. Además, hace que el código sea más limpio, ya que en el constructor de la Vista simplemente establece el DataContext en el ViewModel que se inyectó.

Hablo un poco más sobre esto en la grabación de video de los Patrones de Presentación Separada que Jeremy Miller y yo di en Kaizenconf. La primera parte de la cual se puede encontrar aquí http://www.vimeo.com/2189854 .

Espero que esto ayude, Glenn


Escribí un marco muy ligero donde un ViewModel se resuelve en tiempo de ejecución usando un IoC (Unity) como una extensión de marcado.

El marco permite escribir XAML sin un código atrás, pero aún le permite tener comandos enrutados, enlace de datos y manejadores de eventos.

En cualquier caso, no creo que necesite el XAML suelto en su caso, pero si mira el código ( http://xtrememvvm.codeplex.com ), podría resultar que pueda usar parte del código para resuelva sus propios problemas con la inyección de Ver modelos y servicios.


Glen Block (ver arriba) menciona que un enfoque común es diseñar su solución MVVM para usar DataContext como el lugar donde puede "resolver" su Modelo de Vista en la Vista. Luego puede usar extensiones de diseño de expression mix 2008 (tenga en cuenta que no necesita usar las herramientas de diseño de mezcla de expresiones para aprovechar esto). Por ejemplo:

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=local:MyViewModelMock, IsDesignTimeCreatable=True}"

En su opinión, puede tener un getter de propiedad que arroje su DataContext al tipo que espera (solo para que sea más fácil consumirlo en el código subyacente).

private IMyViewModel ViewModel { get { return (IMyViewModel) DataContext; } }

No olvide utilizar una interfaz para que sus vistas sean más fáciles de probar, o para ayudarlo a implementar diferentes implementaciones de tiempo de ejecución.

En general, no debe resolver cosas del contenedor en su lugar en su solución. En realidad, se considera una mala práctica pasar su contenedor en cada constructor o hacerlo accesible globalmente. (Debe buscar discusiones sobre por qué las estrategias de "Localizador de servicios" constituyen un "Antipatrón").

Cree un constructor de Vista pública con dependencias explícitas que el contenedor (por ejemplo, Prism Unity o MEF) pueda resolver.

Si es necesario, también puede crear un constructor interno predeterminado para crear una simulación de su modelo de vista (o uno real para el caso). Esto protege contra el uso inadvertido de este " constructor de diseño " externamente (en su "Shell" o donde sea). Sus proyectos de prueba también pueden usar dichos constructores usando " InternalsVisibleToAttribute " en " AssemblyInfo ". Pero, por supuesto, eso generalmente no es necesario ya que puedes inyectar tus burlas usando los constructores de dependencia completos de todos modos, y porque la mayoría de tus pruebas deberían centrarse en el modelo de vista en primer lugar. Cualquier código en la Vista idealmente debería ser bastante trivial. (Si su Vista requiere muchas pruebas, entonces quizás quiera preguntarse por qué).

Glen también menciona que puede inyectar vistas en modelos de vista o ver modelos en vistas . Prefiero este último porque hay técnicas perfectamente buenas para desacoplar todo (uso de encuadernación declarativa, comando, agregación de eventos, patrones de mediador, etc.). El modelo de vista es donde se realizará todo el trabajo pesado para organizar la lógica empresarial central. Si todos los puntos de "encuadernación" necesarios son proporcionados por el Modelo de Vista , realmente no debería necesitar NADA sobre la Vista (que en su mayoría se puede conectar de manera declarativa en el XAML).

Si hacemos que el Modelo de Vista sea independiente de la fuente de interacción del usuario, eso hace que sea mucho más fácil de probar (preferiblemente primero). Y también significa que puede conectar fácilmente CUALQUIER vista (WPF, Silverlight, ASP.NET, consola, etc.). De hecho, para garantizar que se haya logrado el desacoplamiento adecuado, podemos preguntarnos si una arquitectura "MVM" (Model-ViewModel) podría funcionar en el contexto de, digamos, un servicio de Workflow. Cuando te paras a pensarlo, la mayoría de las pruebas de tu unidad probablemente se diseñarán según esa premisa.


Humm, estamos experimentando un problema similar, estamos esperando una solución que proporcione soporte de tiempo de diseño bajo Expression Blend 2.0 (Strong Type). Además, estamos esperando una solución para tener disponible una muestra de datos de Mock + Auto-Generated en Expression Blend.

Por supuesto, también estamos buscando que todo funcione con un patrón IOC.

Paul Stovell como un interesante artículo para comenzar: http://www.paulstovell.com/blog/wpf-dependency-injection-in-xaml

Así que intento agregar un soporte de tiempo de diseño más valioso para el objeto de vinculación y burla en el momento del diseño. En este momento estoy teniendo la mayor parte de mi problema relacionado con una conexión sólida entre la vista (código) y ModelView ( Xaml), probé un par de escenarios:

1.) Solución 1: usar genérico para crear la vista

public class MyDotNetcomponent&lt;T&gt; : SomeDotNetcomponent { // Inversion of Control Loader… // Next step add the Inversion of control manager plus // some MockObject feature to work under design time public T View {Get;} }

Esta solución no funciona, ya que Blend no es compatible con el genérico en su interior, es la superficie de diseño, pero Xaml sí tiene algo, funciona bien en tiempo de ejecución pero no en el diseño;

2.) Solución 2: ObjectDataProvider

<ObjectDataProvider ObjectType="{x:Type CP:IFooView}" /> <!-- Work in Blend --> <!—- IOC Issue: we need to use a concrete type and/or static Method there no way to achive a load on demande feature in a easy way -->

3.) Solución 3: heredar ObjectDataProvider

<CWD:ServiceObjectDataProvider ObjectType="{x:Type CP:IFooView}" /> <!-- Cannot inherit from ObjectDataProvider to achive the right behavior everything is private-->

4.) Solución 4: crear un objeto ObjectDataProvider falso desde cero al trabajo

<CWD:ServiceObjectDataProvider ObjectType="{x:Type CP:IFooView }" /> <!-- Not working in Blend, quite obvious-->

5.) Solución 5: Crear una extensión de marcado (Paul Stovell)

<CWM:ServiceMarkup MetaView="{x:Type CP:IFooView}"/> <!-- Not working in Blend -->

Para aclarar un punto cuando dije "no trabajar en combinación" me refiero a que el diálogo de Encuadernación no es utilizable y el diseñador necesita escribir a mano el XAML por sí mismo.

Nuestro siguiente paso probablemente sea tomarse el tiempo para evaluar la capacidad de crear un complemento para Expression Blend.


Sí, lo hacemos todo el tiempo. Puede "inyectar" su ViewModel en el DataContext del control.

De hecho, encuentro que WPF es aún más fácil de usar con DI. Incluso los objetos y las propiedades de la dependencia funcionan sin problemas.