具有依赖注入的 MVVM ViewModel 中的陈旧数据

发布于 2025-01-06 07:40:17 字数 726 浏览 3 评论 0原文

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

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

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

这样做有以下缺点:

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

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

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

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

In my WPF application I use the MVVM pattern together with dependency injection.

The ViewModels that prepare data from the database get the repository injected into the constructor. They also populate the properties with the data from the repository in the constructor.

The ViewModels are all created in the static constructor of the ViewModelLocator class that all Views use to bind to their ViewModel.

This has the following disadvantages:

  1. The data in the Views is never updated, not even when closing and re-opening them, because the ViewModel instance is always the same.
  2. Upon opening the first view, all ViewModels are instantiated and the data they require loaded from the database.

I can think of two ways to solve these problems:

  1. Make every ViewModel implement a method that reads the data from the database and initializes the properties - instead of doing so in the constructor. This would require calling that method every time a view is opened. This introduces temporal coupling which I don't like.
  2. Implement the ViewModelLocator in such a way that it creates the requested ViewModel each time the corresponding property on the ViewModelLocator is called. I don't like this method, because my composition root wouldn't be executed at the start up of the program but would be spread throughout the life time of the program instance.

Is there another way to solve this problem? How are others solving this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

巷雨优美回忆 2025-01-13 07:40:17

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

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

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

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

例如,使用 MEF,您可以将导入切换为使用 ExportFactory而不是直接使用它们的类型。以及非共享创建策略,您可以根据需要构建 ViewModel,并始终使用新数据,而不会出现时间耦合问题。

Implement the ViewModelLocator in such a way that it creates the requested ViewModel each time the corresponding property on the ViewModelLocator is called.

This is more of the approach that I typically take in situations like this. However, instead of having the ViewModelLocator composed via DI of ViewModels, I compose factories that create the ViewModel.

I don't like this method, because my composition root wouldn't be executed at the start up of the program but would be spread throughout the life time of the program instance.

This gets "solved", at least partially, by having the composition compose factories instead of the types themselves. The composition happens once at startup, but creation can occur at any time of the ViewModel in question.

For example, using MEF, you can switch your imports around to use ExportFactory<T> instead of their type directly. Along with a NonShared Creation Policy, you can construct ViewModels, as needed, and always work with fresh data, without the temporal coupling problems.

谢绝鈎搭 2025-01-13 07:40:17

我的抽象ViewModelBase 基类需要一个抽象 RefreshDataCore() 方法。可以通过在 ViewModel 实例上调用 Refresh() 或设置 IsDirty 标志来手动调用此方法。当 ViewModel.IsVisible 为 true 且 IsDirty 设置为 Refresh() 时,也会调用 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();
         }
     }

}

My abstractViewModelBase base class requires an abstract RefreshDataCore() method. This method can be manually invoked by calling Refresh() on the ViewModel instance or by setting an IsDirty flag. When the ViewModel.IsVisible is true and IsDirty is set Refresh() will also be called.

This way you can have lazy refreshing of data whenever your viewmodels become visible and you can also manually invoke a refresh by calling Refresh().

Example below. (I have left INPC notifications out for simplicity)

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

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文