使用ServiceLocator Bootstrapper在MVVM应用程序中注册事件处理程序的正确位置?

时间:2015-06-05 06:46:41

标签: .net design-patterns mvvm event-handling service-locator

注册事件处理程序以将事件从硬件层传播到数据层到UI层的推荐位置在哪里?情况如下:

  • 在C#/ .NET中的应用程序
  • 使用MVVM模式
  • 使用使用Microsofts Unity框架实现的ServiceLocator实现引导程序

简化,有三个层,在程序集中分开:

  • UI.dll(UI层,包含ViewModel和View)
  • Data.dll(模型层)
  • HardwareAbstraction.dll(硬件层)

入口点是starter.exe,包含引导程序并设置ServiceLocator。其他库使用该ServiceLocator。然后,UI.dll从ServiceLocator获取数据接口。

在ViewModel中,只要数据从硬件到达,就需要显示一些数据。数据流是:像硬件一样的扫描仪 - > HardwareAbstraction.dll - > Data.dll - > UI.dll。从一个dll到下一个dll,将触发一个事件以通知侦听器新数据已到达。

在这些活动中注册活动处理程序的最佳位置在哪里?

目前在应用程序中没有真正的概念,很多是在构造函数中完成的,但对我来说似乎并不正确。将它们放在构造函数中,经常会破坏我的单元测试(在现有应用程序中引入单元测试)并设计时间ViewModels(在C#中,而不是Xaml)。

1 个答案:

答案 0 :(得分:1)

一般来说,我会尽量避免使用全局服务定位器。我认为这是一种反模式。不要在viewModels中使用它。它将测试一只熊。只需在构造函数中传递依赖项。我为每个应用程序都有一个主要的IoC容器:我的主要引导程序。然后,我在一些工厂注册的人少得多。我从不暴露容器。这个计划是在经历了多年的失踪和过时的依赖之后发生的。

viewModel级别应该使用PropertyChanged和ObservableCollection来暴露其事件。如果你能提供帮助,没有人应该以编程方式注册;它们都应该在UI级别的绑定中使用。硬件抽象只是模型级实体的另一种形式。它是(数据来源)。您可以拥有多个数据/模型层。

在(viewModel或模型管理器)构造函数中注册模型级事件是正确的做法。在Dispose中清理它们。为了使其更清洁,我做的一件事就是在有人注册时触发模型中的事件。它看起来像这样:

class ModelManager: IModelManager {
  ...
  private Action<IModel> _modelAdded = delegate {};
  public event Action<IModel> ModelAdded {
     add { 
        _modelAdded = Delegate.Combine(_modelAdded, value);
        foreach(var model in Models)
           value.Invoke(model);
     }
     remove { ... }
  }
  ...
}

class ModelManagerVM: ViewModelBase {
   public ModelManagerVM(IModelManager mgr, IModelVMFactory factory) {
      _factory = factory;
      _mgr = mgr;
      _mgr.ModelAdded += OnModelAdded; // triggers on subscribe
   }
   private void OnModelAdded(IModel model) { // never virtual
      // use a dispatcher to push this to the UI thread
      var existing = _modelVMs.FirstOrDefault(m => m.ID == model.ID);
      if (existing != null) existing.Model = model;
      else _modelVMs.Add(_factory.Create(model));
   }
   public void Dispose() { 
      _mgr.ModelAdded -= OnModelAdded; // always unsubscribe on passed-in deps
   }
}
相关问题