2010-05-10 8 views
10

Estoy usando inyección de dependencia de constructor en mi aplicación WPF y sigo corriendo en el siguiente patrón, por lo que me gustaría obtener la opinión de otras personas y escuchar sobre soluciones alternativas.¿Cómo uso la inyección de dependencia del constructor para suministrar Modelos de una colección a sus Modelos de Vista?

El objetivo es conectar una jerarquía de ViewModels a una jerarquía de modelos similar, de modo que la responsabilidad de presentar la información en cada modelo recaiga en su propia implementación de ViewModel. (El patrón también aparece en otras circunstancias, pero MVVM debería ser un buen ejemplo.)

Aquí hay un ejemplo simplificado. Dado que tengo un modelo que tiene una colección de más modelos:

public interface IPerson 
{ 
    IEnumerable<IAddress> Addresses { get; } 
} 

public interface IAddress 
{ 
} 

me gustaría reflejar esta jerarquía en los ViewModels para que pueda enlazar un cuadro de lista (o lo que sea) a una colección en la Persona modelo de vista:

public interface IPersonViewModel 
{ 
    ObservableCollection<IAddressViewModel> Addresses { get; } 
    void Initialize(); 
} 

public interface IAddressViewModel 
{ 
} 

el niño modelo de vista tiene que presentar la información del modelo del niño, por lo que se inyecta a través del constructor:

public class AddressViewModel : IAddressViewModel 
{ 
    private readonly IAddress _address; 

    public AddressViewModel(IAddress address) 
    { 
     _address = address; 
    } 
} 

la pregunta es, ¿cuál es la mejor manera de para suministrar el modelo hijo al ViewModel hijo correspondiente?

El ejemplo es trivial, pero en un caso real típico los ViewModels tienen más dependencias, cada una de las cuales tiene sus propias dependencias (y así sucesivamente). Estoy usando Unity 1.2 (aunque creo que la pregunta es relevante en los otros contenedores de IoC), y estoy usando las estrategias de vista de Caliburn para buscar y cablear automáticamente la Vista apropiada a un ViewModel.

Aquí está mi solución actual:

El padre ViewModel necesita para crear un niño modelo de vista para cada modelo del niño, por lo que tiene una fábrica método añadido a su constructor que se utiliza durante la inicialización:

public class PersonViewModel : IPersonViewModel 
{ 
    private readonly Func<IAddress, IAddressViewModel> _addressViewModelFactory; 
    private readonly IPerson _person; 

    public PersonViewModel(IPerson person, 
          Func<IAddress, IAddressViewModel> addressViewModelFactory) 
    { 
     _addressViewModelFactory = addressViewModelFactory; 
     _person = person; 

     Addresses = new ObservableCollection<IAddressViewModel>(); 
    } 

    public ObservableCollection<IAddressViewModel> Addresses { get; private set; } 

    public void Initialize() 
    { 
     foreach (IAddress address in _person.Addresses) 
      Addresses.Add(_addressViewModelFactory(address)); 
    } 
} 

Un método de fábrica que cumple con la interfaz Func<IAddress, IAddressViewModel> está registrado con el UnityContainer principal. El método de fábrica utiliza un contenedor secundario para registrar la dependencia IAddress que es requerido por el modelo de vista y luego se resuelve el modelo de vista del niño:

public class Factory 
{ 
    private readonly IUnityContainer _container; 

    public Factory(IUnityContainer container) 
    { 
     _container = container; 
    } 

    public void RegisterStuff() 
    { 
     _container.RegisterInstance<Func<IAddress, IAddressViewModel>>(CreateAddressViewModel); 
    } 

    private IAddressViewModel CreateAddressViewModel(IAddress model) 
    { 
     IUnityContainer childContainer = _container.CreateChildContainer(); 

     childContainer.RegisterInstance(model); 

     return childContainer.Resolve<IAddressViewModel>(); 
    } 
} 

Ahora, cuando el PersonViewModel se inicializa, se realiza un bucle a través de cada Address en el modelo y las llamadas CreateAddressViewModel() (que se inyectó a través del argumento Func<IAddress, IAddressViewModel>). CreateAddressViewModel() crea un contenedor secundario temporal y registra el modelo IAddress para que cuando resuelva el IAddressViewModel del contenedor secundario, el AddressViewModel obtenga la instancia correcta inyectada a través de su constructor.

Esto parece ser una buena solución para mí ya que las dependencias de ViewModels son muy claras y son fácilmente comprobables y no tienen conocimiento del contenedor de IoC. Por otro lado, el rendimiento es bueno pero no excelente ya que se pueden crear muchos contenedores temporales para niños. También termino con muchos métodos de fábrica muy similares.

  • ¿Es esta la mejor manera de inyectar los niños en los modelos ViewModels niño con la Unidad?
  • ¿Hay una manera mejor (o más rápido) que hacerlo en otros recipientes COI, por ejemplo, Autofac?
  • ¿Cómo este problema deben abordarse MEF, dado que no es un contenedor IoC tradicional, pero todavía se utiliza para componer objetos?

Respuesta

2

Según el contenedor, ¿no puede especificar un parámetro (nombrado o no) en el método CreateAddressViewModel de su fábrica?

container.Resolve<IAddressViewModel>(new NamedParameterOverloads() { { "Address", model } }; 

Dependiendo del contenedor de su fábrica puede tener que saber el nombre del parámetro (TinyIoC y Castle que yo sepa), o puede tenía que ser el último en la lista de dependencias de creación (tu caso es distinto dependiendo de contenedores), los cuales no es genial, pero ahorra la creación de muchos contenedores para niños en rápida sucesión, y la agitación de GC que seguirá, y aún obtienes DI para todas tus otras dependencias.

Por supuesto, esto cae si su máquina virtual también tiene una dependencia que requiere la misma dirección I, en ese caso un contenedor secundario es probablemente el camino a seguir a menos que desee que la máquina virtual tenga conocimiento del contenedor.

Actualización: Si estás utilizando un subcontenedor de un contenedor que utiliza "gana último registro" (que creo que hace la Unidad), entonces usted podría pasar el mismo niño recipiente en su fábrica cada vez, y tienen su fábrica de simplemente registrar el nuevo IAddress - de ese modo no sería la creación de una nueva instancia UnityContainer en el montón para cada iteración y debe reducir en colecciones de basura si va a crear un montón de artículos.

+0

Como usted señala la especificación de parámetros no funciona si cualquiera de las dependencias necesita el modelo, que ha sido un sensacional para mí. Sin embargo, es posible reutilizar el contenedor para niños. – GraemeF

0

La aplicación de ejemplo modelo de vista de la WPF Application Framework (WAF) muestra cómo se podría llevar el modelo y el modelo de vista juntos. La muestra usa MEF como marco de inyección de dependencias.

+0

Mirando a través de las muestras que no he encontrado en cualquier lugar que hace esto - que tienden a crear un único modelo de vista y establecer el modelo sobre como cambia la selección. Tal vez no estoy buscando en el lugar correcto, ¿puedes señalarme la clase que tenías en mente? – GraemeF

Cuestiones relacionadas