2016-05-12 14 views
-1
erstellt

Ich entwickle eine C#/WPF/MVVM/UWP-App, die eine ViewModelLocator verwendet, die wie folgt aussieht:sicher, dass eine statische Klasse vor den anderen

public class ViewModelLocator 
{ 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", 
     "CA1822:MarkMembersAsStatic", 
     Justification = "This non-static member is needed for data binding purposes.")] 
    public MainPageViewModel MainPage 
    { 
     get 
     { 
      return ServiceLocator.Current.GetInstance<MainPageViewModel>(); 
     } 
    } 


    static ViewModelLocator() 
    { 
     // DEBUG LINE: var test = Views.ViewLocator.MainPageKey; 
     ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); 

     if (ViewModelBase.IsInDesignModeStatic) 
     { 
      SimpleIoc.Default.Register<IDataService, Design.DesignDataService>(); 
     } 
     else 
     { 
      SimpleIoc.Default.Register<IDataService, DataService>(); 
     } 

     SimpleIoc.Default.Register<MainPageViewModel>(); 
    } 
} 

Ich habe eine andere Klasse, eine ViewLocator, für die Navigation der wie folgt aussieht:

public class ViewLocator 
{ 
    public static readonly string MainPageKey = typeof(MainPage).Name; 
    public static readonly string WorkPageKey = typeof(WorkPage).Name; 

    static ViewLocator() 
    { 
     ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default); 

     var navigationService = new NavigationService(); 

     navigationService.Configure(MainPageKey, typeof(MainPage)); 
     navigationService.Configure(WorkPageKey, typeof(WorkPage)); 

     SimpleIoc.Default.Register<INavigationService>(() => navigationService); 

     SimpleIoc.Default.Register<IDialogService, DialogService>(); 
    } 
} 

Bevor wurden beide Klassen in ViewModelLocator kombiniert, jedoch dachte ich, dass der ViewModelLocator als Teil der „Viewmodels-Side-of-Dinge“ soll die Ansichten nicht bewusst sein und Es sind Typen, weshalb ich diesen Code in zwei Klassen umstrukturiert habe.

Mein MainPageView dann eine Taste hat, die einen Navigationsbefehl in den MainPageView.cs löst

public class MainPageViewModel : ViewModelBase 
{ 
    private INavigationService _navigationService; 

    public RelayCommand CreateNewImageCommand { get; private set; } 

    public MainPageViewModel(INavigationService navigationService) 
    { 
     _navigationService = navigationService; 

     CreateNewImageCommand = new RelayCommand(CreateNewImage,() => true); 
    } 

    public void CreateNewImage() 
    { 
     _navigationService.NavigateTo(Views.ViewLocator.WorkPageKey); 
    } 
} 

Für Vollständigkeit, hier ist mein App.xaml

<Application 
...> 

<Application.Resources> 
    <v:ViewLocator x:Key="ViewLocator" /> 
    <vm:ViewModelLocator x:Key="ViewModelLocator" /> 
</Application.Resources> 

Nun, was passiert ist das: wenn ich nicht die DEBUG LINE in ViewModelLocator habe, an dem Punkt, an dem MainPage sein ViewModel vom ViewModelLocator anfordert, hat der Konstruktor von ViewLocator wurde noch nicht aufgerufen und return ServiceLocator.Current.GetInstance<MainPageViewModel>(); löst eine Ausnahme aus. Wenn ich die DEBUG-LINE einschließe, erzwingt das, dass der ViewLocator-Konstruktor zuerst ausgeführt wird, und alles funktioniert gut.

Wie kann ich dieses Verhalten ohne diese seltsame Debug-Zeile erreichen?

+0

Ich werde nur hier verlassen [ist ServiceLocator Anti-Muster] (http://stackoverflow.com/questions/22795459/is-servicelocator-anti-pattern) – Liam

+0

TL; DR Sie sollten wirklich Dependency verwenden Injektion nicht Service Locator Muster. Wenn man von vorne anfängt, ist es normalerweise ein viel besseres und einfacheres Muster zu verstehen. – Liam

+0

@Liam Ich bin kein Fan von IoC Containern oder ServiceLocator oder wie immer man es nennen möchte. Ich habe nur untersucht, wie man VM und V richtig trennt, und dies ist der einzige Arbeitsansatz, der mich bisher zufriedenstellt. Ich bin jedoch sehr offen für die Verwendung von DI - wenn Sie mir bitte einen Referenzcode zeigen könnten, der es richtig verwendet. – Xaser

Antwort

0

Sie kommen in den Grund, warum die meisten Entwickler keine statischen Klassen und einen Service Locator verwenden.

Wie in Ihren Kommentaren vorgeschlagen, versuchen Sie die Abhängigkeitsinjektion. Dies ermöglicht eine Inversion der Steuerung für die Komponente, die diese Verdrahtung verwaltet.

Schauen Sie in MEF oder Unity als ein Framework zu verwenden, anstatt Ihren eigenen IoC zu rollen. Beide haben Vor- und Nachteile, aber beide werden funktionieren. Sie ermöglichen auch einfachere und sauberere Tests.

MEF

Unity

0

Trotz aller Kommentare und anderen Antworten, denke ich, dass ich auf der Strecke nach war alles. Um dieses Problem zu lösen oder besser umgehen zu können, habe ich im Startup der App das Bootstrapping des SimpleIoC Containers erstellt und den Code aus dem ViewLocator Konstruktor dorthin verschoben.

Ich entfernte auch die ServiceLocator Anrufe, die Art von redundant war und jetzt auf SimpleIoC.Default direkt verlassen.

Der Navigator wird vom Container SimpleIoC weiterhin in die ViewModels eingefügt. Der ViewModelLocator ist jedoch in Verbindung mit dem XAML-Code notwendig.

Ich empfehle diese Antwort als Ausgangspunkt für andere, die auch mit diesem Problem kämpfen.

https://stackoverflow.com/a/25524753/2175012

Schließlich habe ich die Seite genommen, die ServiceLocators kein anti-Muster sind, sondern müssen sorgfältig verwendet werden.