2010-05-10 12 views
10

Korzystam z wtrysku zależnego od konstruktora w mojej aplikacji WPF i nadal używam następującego schematu, więc chciałbym poznać zdanie innych ludzi na ten temat i usłyszeć o alternatywnych rozwiązaniach.Jak używać wtrysku zależnego od konstruktora do dostarczania modeli z kolekcji do ich modeli ViewModels?

Celem jest połączenie hierarchii ViewModels z podobną hierarchią modeli, aby odpowiedzialność za prezentowanie informacji w każdym modelu spoczywała na własnej implementacji ViewModel. (Wzór ten pojawia się również w innych okolicznościach, ale MVVM powinna stanowić dobry przykład.)

Oto uproszczony przykład. Biorąc pod uwagę, że mam model, który posiada kolekcję kolejnych modeli:

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

public interface IAddress 
{ 
} 

Chciałbym odzwierciedlać tę hierarchię w ViewModels tak, że mogę powiązać ListBox (lub cokolwiek) do kolekcji w osobie ViewModel:

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

public interface IAddressViewModel 
{ 
} 

dziecko ViewModel musi przedstawić informacje od dziecka model, więc jest wstrzykiwany za pomocą konstruktora:

public class AddressViewModel : IAddressViewModel 
{ 
    private readonly IAddress _address; 

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

Chodzi o to, co jest najlepszym sposobem dostarczyć model dziecka do odpowiedniego dziecka ViewModel?

Przykład jest banalny, ale w typowym przypadku rzeczywistym modele ViewModels mają więcej zależności - z których każda ma swoje własne zależności (i tak dalej). Używam Unity 1.2 (chociaż myślę, że pytanie dotyczy innych kontenerów IoC) i używam strategii widoku Caliburn, aby automatycznie znaleźć i podłączyć odpowiedni View do ViewModel.

Oto mój obecny rozwiązanie:

Rodzic ViewModel musi stworzyć dziecku ViewModel dla każdego modelu dziecięcej, więc ma fabrykę metoda dodaną do jego konstruktora których używa podczas inicjalizacji:

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)); 
    } 
} 

Metoda fabryczna, która spełnia wymagania interfejsu Func<IAddress, IAddressViewModel>, jest zarejestrowana w głównej wersji UnityContainer. Metoda fabryka wykorzystuje pojemnik dziecko zarejestrować zależność IAddress, który jest wymagany przez ViewModel a następnie rozwiązuje ViewModel dziecko:

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>(); 
    } 
} 

Teraz, gdy PersonViewModel jest inicjowany, to pętle przez każdy Address w modelu i połączeń CreateAddressViewModel() (który został wprowadzony za pomocą argumentu Func<IAddress, IAddressViewModel>). CreateAddressViewModel() tworzy tymczasowy kontener podrzędny i rejestruje model IAddress, dzięki czemu po rozwiązaniu IAddressViewModel z kontenera podrzędnego AddressViewModel otrzymuje poprawną instancję wstrzykniętą za pośrednictwem jej konstruktora.

To wydaje się być dobrym rozwiązaniem dla mnie, ponieważ zależności ViewModels są bardzo jasne i są łatwe do sprawdzenia i nieświadome pojemnika IoC. Z drugiej strony wydajność jest w porządku, ale nie jest tak duża, ponieważ można utworzyć wiele tymczasowych kontenerów podrzędnych. Również kończę z wieloma bardzo podobnymi metodami fabrycznymi.

  • Czy to najlepszy sposób, aby wstrzykiwać dziecku Modele do dziecka ViewModels with Unity?
  • Czy jest lepszy (lub szybszy) sposób, aby to zrobić w innych kontenerach IoC, np. Autofac?
  • W jaki sposób problem ten został rozwiązany za pomocą MEF, biorąc pod uwagę, że nie jest to tradycyjny kontener IoC, ale nadal jest używany do komponowania obiektów?

Odpowiedz

2

W zależności od kontenera nie można podać parametru (nazwanego lub nie) w metodzie CreateAddressViewModel w swojej fabryce?

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

zależności od pojemnika swoją fabryka może znać nazwę parametru (TinyIoC i Castle AFAIK) lub może to miał być ostatni na liście zależności konstruktora (YMMV zależności kontenerach), które nie jest świetny, ale oszczędza, tworząc wiele kontenerów dla dzieci w krótkich odstępach czasu, a GC będzie je szarpać, a nadal otrzymasz DI dla wszystkich twoich innych zależności.

Oczywiście to spada, jeśli twoja maszyna wirtualna ma również zależność wymagającą tego samego adresu IAddress, w takim przypadku kontener dla dzieci jest prawdopodobnie drogą do wyjścia, chyba że chcesz, aby maszyna wirtualna posiadała wiedzę o kontenerze.

Aktualizacja: Jeśli używasz subcontainer pojemnika, który używa „wygrywa ostatni register” (co moim zdaniem Unity robi), a następnie można przejść tę samą dziecko pojemnik w fabryce za każdym razem, i mają Twoja fabryka po prostu rejestruje nowy adres IAddress - w ten sposób nie tworzyłbyś nowej instancji UnityContainer na stercie dla każdej iteracji, a przy tworzeniu wielu elementów powinna ona zmniejszyć ilość śmieci.

+0

Jak podkreślasz, określanie parametrów nie działa, jeśli którakolwiek z zależności wymaga modelu, który był dla mnie showstopper. Ponowne użycie kontenera podrzędnego jest jednak możliwe. – GraemeF

0

Przykładowa aplikacja ViewModel modelu WPF Application Framework (WAF) pokazuje, w jaki sposób można połączyć model i ViewModel. Próbka używa MEF jako podstawy iniekcji Dependency.

+0

Przeglądając próbki, których nie znalazłem nigdzie, które to robią, mają tendencję do tworzenia pojedynczego ViewModelu i ustawiania Modelu na nim w miarę zmiany wyboru. Może nie szukam we właściwym miejscu, czy możesz wskazać mi klasę, którą miałeś na myśli? – GraemeF

Powiązane problemy