【问题标题】:Stale data in MVVM ViewModels with dependency injection具有依赖注入的 MVVM ViewModels 中的陈旧数据
【发布时间】:2012-03-06 03:56:38
【问题描述】:

在我的 WPF 应用程序中,我将 MVVM 模式与依赖注入一起使用。

从数据库准备数据的 ViewModel 将存储库注入到构造函数中。它们还使用构造函数中存储库中的数据填充属性。

ViewModel 都是在 ViewModelLocator 类的静态构造函数中创建的,所有 View 都使用该构造函数绑定到它们的 ViewModel。

这样有以下缺点:

  1. 视图中的数据永远不会更新,即使在关闭和重新打开它们时也不会更新,因为 ViewModel 实例始终相同。
  2. 在打开第一个视图时,所有 ViewModel 都会被实例化,并且它们需要从数据库中加载数据。

我可以想到两种方法来解决这些问题:

  1. 让每个 ViewModel 实现一个从数据库读取数据并初始化属性的方法 - 而不是在构造函数中这样做。这将需要在每次打开视图时调用该方法。这引入了我不喜欢的temporal coupling
  2. 以这样一种方式实现 ViewModelLocator:每次调用 ViewModelLocator 上的相应属性时,它都会创建请求的 ViewModel。我不喜欢这种方法,因为我的组合根不会在程序启动时执行,而是会分布在程序实例的整个生命周期中。

还有其他方法可以解决这个问题吗?其他人是如何解决这个问题的?

【问题讨论】:

    标签: c# .net wpf mvvm dependency-injection


    【解决方案1】:

    以这样一种方式实现 ViewModelLocator:每次调用 ViewModelLocator 上的相应属性时,它都会创建请求的 ViewModel。

    这更多是我在这种情况下通常采用的方法。但是,我没有通过 ViewModel 的 DI 组合 ViewModelLocator,而是组合了创建 ViewModel 的工厂。

    我不喜欢这种方法,因为我的组合根不会在程序启动时执行,而是会分布在程序实例的整个生命周期中。

    这可以通过让组合组成工厂而不是类型本身来“解决”,至少部分是这样。组合在启动时发生一次,但创建可以在相关 ViewModel 的任何时间发生。

    例如,使用 MEF,您可以切换导入以使用 ExportFactory<T> 而不是直接使用它们的类型。除了NonShared Creation Policy,您还可以根据需要构建 ViewModel,并始终使用新数据,而不会出现时间耦合问题。

    【讨论】:

    • 感谢您的回答。为我的每个 ViewModel 建立一个工厂似乎有点矫枉过正,因为这些工厂只能在一个地方使用,目前没有必要。此外,除了container.Resolve 之外,他们不会做任何事情。由于 ViewModelLocator 仍然是唯一使用容器的地方,我更喜欢调用 container.Resolve 而不是抽象工厂,因为即使在这种情况下,容器也不会泄漏到应用程序代码中。我能想到的抽象工厂的唯一优势是在启动时会立即出现错误(续)
    • (续)如果容器的配置不正确。
    【解决方案2】:

    我的 abstractViewModelBase 基类需要一个抽象 RefreshDataCore() 方法。可以通过在 ViewModel 实例上调用 Refresh() 或设置 IsDirty 标志来手动调用此方法。当 ViewModel.IsVisible 为 true 并设置 IsDirty 时,也会调用 Refresh()。

    这样,您可以在视图模型可见时延迟刷新数据,也可以通过调用 Refresh() 手动调用刷新。

    示例如下。 (为简单起见,我省略了 INPC 通知)

    public abstract class ViewModelBase
    {
         //Pull your data from the repository here
         protected abstract void RefreshCore();
         public void Refresh()
         {
                RefreshCore();
                IsDirty = false;
         }
    
         private bool _isVisible = false;
         //DataBind this to the visibility of element "hosting" your view model
         public bool IsVisible
         {
             get { return _isVisible; }
             set
             {
                  if (_isVisible == value)
                      return;
    
    
                  _isVisible = value;
                  if (IsVisible && IsDirty)
                       Refresh();
             }
         }
    
         private bool _isDirty = true;
         public bool IsDirty 
         {
             get { return _isDirty; }
             set 
             {
                if (_isDirty == value)
                   return;
    
                _isDirty = value;
                if (IsVisible && IsDirty)
                   Refresh();
             }
         }
    
    }
    

    【讨论】:

    • 如何解决时间耦合问题?你只是忍受它,并且总是根据需要调用刷新吗?
    • 我有一个 ApplicationViewModel,它会定期检查数据的状态并设置适当的 IsDirty 标志(但只有​​可见的视图模型会主动刷新)出于性能原因,ApplicationViewModel 还包含数据的本地缓存其他 ViewModelBase 实例在其 RefreshDataCore() 方法中读取。您可以根据需要完全灵活地实施此操作
    • 感谢您的回答。但是,我没有看到时间耦合解决了,所以这不是我要使用的。
    • 我们使用类似的方法。后台线程不断地轮询服务以获取新/脏数据。请注意,我们的应用不是 LoB 应用。我们的大部分数据从未保存过,而且几乎总是在变化。
    猜你喜欢
    • 1970-01-01
    • 2021-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多