如何从ServiceLocator移至依赖注入?具体示例

问题描述 投票:0回答:1

问题正在从ServiceLocator反模式转移到Dependency Injection。鉴于我的经验不足,我无法将DI原则转换为现在实现的代码。

摘要

“摘要”部分是可选的,可供阅读。您可能想对某事发表评论,建议。

该程序的主要目的是合并特定信息的占位符字段的过程。信息数量众多,因此需要具有基础架构。例如表格,服务,数据库。我对此任务有一些经验。我设法基于WinForms创建了一个类似的程序。而且它甚至有效!但是就模式,维护,可扩展性和性能而言,代码很糟糕。重要的是要了解编程是一种爱好。它不是主要的教育或工作。

WinForms上执行所描述任务的经验很糟糕。我开始研究模式和新平台。起点是UWPMVVM。应该指出的是,绑定机制是惊人的。

途中的第一个问题是独立解决的。这与通过UWP连接的NavigationView中的ShellPageShellViewModel中导航有关。以及创建一个NavigationService。它基于Windows Template Studio中的模板。

由于存在有效的WinForms程序及其反模式方向,所以有时间和渴望去做[[一切正确。

现在,我面临架构问题。称为ServiceLocator(或ViewModelLocator)。我在Microsoft的示例中找到了它,包括Windows Template Studio的模板。这样,我便陷入了反模式陷阱。如上所述,我不想再这样。

解决方案的第一件事就是dependency injection。鉴于我的经验不足,我无法将DI原则转换为现在实现的代码。

当前实施

应用UWP的起点是app.xaml.cs。重点是将控制权转移到ActivationService。它的任务是将Frame添加到Window.Current.Content并导航到默认页面-MainPageMicrosoft documentation

ViewModelLocator是单例。对其属性的第一次调用将调用构造函数。

private static ViewModelLocator _current; public static ViewModelLocator Current => _current ?? (_current = new ViewModelLocator()); // Constructor private ViewModelLocator(){...}

ViewModelLocatorViewPage)一起使用,ShellPage

private ShellViewModel ViewModel => ViewModelLocator.Current.ShellViewModel;

ViewModelLocatorViewModel的使用类似,ShellViewModel

private static NavigationService NavigationService => ViewModelLocator.Current.NavigationService;

移至DI

ShellViewModel具有来自NavigationServiceViewModelLocator,如上所述。我现在如何去DI?实际上,程序很小。现在是摆脱反模式的好时机。

代码

ViewModelLocator

private static ViewModelLocator _current; public static ViewModelLocator Current => _current ?? (_current = new ViewModelLocator()); private ViewModelLocator() { // Services SimpleIoc.Default.Register<NavigationService>(); // ViewModels and NavigationService items Register<ShellViewModel, ShellPage>(); Register<MainViewModel, MainPage>(); Register<SettingsViewModel, SettingsPage>(); } private void Register<TViewModel, TView>() where TViewModel : class where TView : Page { SimpleIoc.Default.Register<TViewModel>(); NavigationService.Register<TViewModel, TView>(); } public ShellViewModel ShellViewModel => SimpleIoc.Default.GetInstance<ShellViewModel>(); public MainViewModel MainViewModel => SimpleIoc.Default.GetInstance<MainViewModel>(); public SettingsViewModel SettingsViewModel => SimpleIoc.Default.GetInstance<SettingsViewModel>(); public NavigationService NavigationService => SimpleIoc.Default.GetInstance<NavigationService>();

ShellPage:页面

private ShellViewModel ViewModel => ViewModelLocator.Current.ShellViewModel; public ShellPage() { InitializeComponent(); // shellFrame and navigationView from XAML ViewModel.Initialize(shellFrame, navigationView); }

ShellViewModel:ViewModelBase

private bool _isBackEnabled; private NavigationView _navigationView; private NavigationViewItem _selected; private ICommand _itemInvokedCommand; public ICommand ItemInvokedCommand => _itemInvokedCommand ?? (_itemInvokedCommand = new RelayCommand<NavigationViewItemInvokedEventArgs>(OnItemInvoked)); private static NavigationService NavigationService => ViewModelLocator.Current.NavigationService; public bool IsBackEnabled { get => _isBackEnabled; set => Set(ref _isBackEnabled, value); } public NavigationViewItem Selected { get => _selected; set => Set(ref _selected, value); } public void Initialize(Frame frame, NavigationView navigationView) { _navigationView = navigationView; _navigationView.BackRequested += OnBackRequested; NavigationService.Frame = frame; NavigationService.Navigated += Frame_Navigated; NavigationService.NavigationFailed += Frame_NavigationFailed; } private void OnItemInvoked(NavigationViewItemInvokedEventArgs args) { if (args.IsSettingsInvoked) { NavigationService.Navigate(typeof(SettingsViewModel)); return; } var item = _navigationView.MenuItems.OfType<NavigationViewItem>().First(menuItem => (string)menuItem.Content == (string)args.InvokedItem); var pageKey = GetPageKey(item); NavigationService.Navigate(pageKey); } private void OnBackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args) { NavigationService.GoBack(); } private void Frame_Navigated(object sender, NavigationEventArgs e) { IsBackEnabled = NavigationService.CanGoBack; if (e.SourcePageType == typeof(SettingsPage)) { Selected = _navigationView.SettingsItem as NavigationViewItem; return; } Selected = _navigationView.MenuItems .OfType<NavigationViewItem>() .FirstOrDefault(menuItem => IsMenuItemForPageType(menuItem, e.SourcePageType)); } private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e) { throw e.Exception; } private bool IsMenuItemForPageType(NavigationViewItem item, Type sourcePageType) { var pageKey = GetPageKey(item); var navigatedPageKey = NavigationService.GetNameOfRegisteredPage(sourcePageType); return pageKey == navigatedPageKey; } private Type GetPageKey(NavigationViewItem item) => Type.GetType(item.Tag.ToString());

更新1

我对ServiceLocatorViewModelLocator之间的相等性有误吗?

称为ServiceLocator(或ViewModelLocator

基本上,当前任务是连接到ViewViewModelNavigationService超出了此任务的范围。所以它不应该在ViewModelLocator中吗?

c# mvvm dependency-injection uwp service-locator
1个回答
1
投票
正如@Maess指出的那样,您现在(现在)面临的最大挑战是将静态依赖项重构为构造函数注入。例如,您的ShellViewModel应该具有类似的构造函数:

public ShellViewModel(INavigationService navigation)

一旦完成,您就可以建立一个具有所有依赖关系的DI框架(如NInject)(有点像您的SimpleIoC之类的东西),并且,理想情况下,从容器中拉出一个根对象(它可以构造其他所有东西) 。通常,这是应用程序的主要视图模型。

我已经在WPF和UWP的多个项目上成功做到了这一点,并且效果很好。您唯一需要注意的是在运行时创建视图模型时(通常这样做),方法是注入工厂。

© www.soinside.com 2019 - 2024. All rights reserved.