J'utilise l'injection de dépendances de constructeurs dans mon application WPF et je continue de suivre le modèle suivant, donc j'aimerais avoir l'opinion des autres et entendre parler de solutions alternatives.Comment utiliser l'injection de dépendance de constructeur pour fournir des modèles d'une collection à leurs ViewModels?
L'objectif est de connecter une hiérarchie de ViewModels à une hiérarchie similaire de modèles, de sorte que la responsabilité de présenter les informations dans chaque modèle réside dans sa propre implémentation ViewModel. (Le motif apparaît également dans d'autres circonstances, mais MVVM devrait en faire un bon exemple.)
Voici un exemple simplifié. Étant donné que j'ai un modèle qui a une collection de modèles supplémentaires:
public interface IPerson
{
IEnumerable<IAddress> Addresses { get; }
}
public interface IAddress
{
}
Je voudrais refléter cette hiérarchie dans les ViewModels afin que je puisse lier un ListBox (ou autre) à une collection dans la personne ViewModel:
public interface IPersonViewModel
{
ObservableCollection<IAddressViewModel> Addresses { get; }
void Initialize();
}
public interface IAddressViewModel
{
}
l'enfant ViewModel doit présenter les informations du modèle de l'enfant, il est donc injecté par le constructeur:
public class AddressViewModel : IAddressViewModel
{
private readonly IAddress _address;
public AddressViewModel(IAddress address)
{
_address = address;
}
}
la question est, quelle est la meilleure façon fournir le modèle enfant à l'enfant ViewModel correspondant?
L'exemple est trivial, mais dans un cas réel typique, les ViewModels ont plus de dépendances - chacune ayant ses propres dépendances (et ainsi de suite). J'utilise Unity 1.2 (bien que je pense que la question soit pertinente pour les autres conteneurs IoC), et j'utilise les stratégies de vue de Caliburn pour trouver automatiquement et connecter la View appropriée à un ViewModel.
Voici ma solution actuelle:
Le parent ViewModel a besoin de créer un enfant ViewModel pour chaque modèle de l'enfant, il a donc une méthode usine ajouté à son constructeur qu'il utilise lors de l'initialisation:
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));
}
}
Une méthode d'usine qui satisfait l'interface Func<IAddress, IAddressViewModel>
est enregistrée avec le UnityContainer
principal. La méthode de fabrication utilise un conteneur enfant pour enregistrer la dépendance IAddress
qui est requis par la ViewModel et résout alors le ViewModel de l'enfant:
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>();
}
}
Maintenant, lorsque le PersonViewModel
est initialisé, il boucle à travers chaque Address
dans le modèle et les appels CreateAddressViewModel()
(qui a été injecté via l'argument Func<IAddress, IAddressViewModel>
). CreateAddressViewModel()
crée un conteneur enfant temporaire et enregistre le modèle IAddress
de sorte que lorsqu'il résout le IAddressViewModel
à partir du conteneur enfant, le AddressViewModel
obtient l'instance correcte injectée via son constructeur.
Cela semble être une bonne solution pour moi, car les dépendances des ViewModels sont très claires et elles sont facilement testables et ne connaissent pas le conteneur IoC. D'un autre côté, les performances sont correctes, mais pas excellentes, car beaucoup de conteneurs enfants temporaires peuvent être créés. Aussi, je me retrouve avec beaucoup de méthodes d'usine très similaires. Est-ce la meilleure façon d'injecter l'enfant? Modèles dans l'enfant ViewModels avec Unity?
- Y a-t-il une meilleure façon (ou plus rapide) de le faire dans d'autres conteneurs IoC, par ex. Autofac?
- Comment ce problème serait-il traité avec MEF, étant donné qu'il ne s'agit pas d'un conteneur IoC traditionnel mais qu'il est toujours utilisé pour composer des objets?
Comme vous le faites remarquer, la spécification de paramètres ne fonctionne pas si l'une des dépendances nécessite le modèle, ce qui a été un coup de projecteur pour moi. Réutiliser le conteneur enfant est une possibilité, cependant. – GraemeF