设计:如何通知控制器有关跨应用程序的数据修改

本文关键字:应用程序 修改 数据 控制器 何通知 通知 设计 | 更新日期: 2023-09-27 18:37:14

在基于 mvc 的大型系统中,有负责编辑数据的视图和显示该数据的视图。

示例:UserManagementViewUserSelectionView

在另一个子系统对相同数据进行更改后,每个子系统都应该知道它是否需要数据更新,以便其控制器知道是否需要数据更新。

我对此的想法类似于观察者模式(有点集成在 c# 中),所有控制器都是侦听器,最终会收到发生数据操作的通知。

例如,BindingList<>提供了一个ListChanged事件。同样,可以为每个数据结构创建一个接口,并将更改通知控制器。这会增加开销(IMO),我发现这个解决方案很难在大系统中维护,而且无论如何更新数据都不是解决方案。

在这种情况下,哪种体系结构设计可以提供帮助?

设计:如何通知控制器有关跨应用程序的数据修改

这个问题听起来像是你试图在没有模型部分的情况下使用 MVC。如果我误解了,编辑您的问题以包含实际用例(示例)可能有助于我们理解上下文。

但一般来说,您的控制器中不应真正保留/存储任何内容。因此,控制器中不应该有任何需要"更新"或"通知"的内容(即:没有数据)。相反,数据应该位于管理所有数据的单独"模型"层中。然后,视图从模型层读取以获取该视图的任何数据。

要快速复习一下,请查看 MVC 上的维基百科页面,该页面有一个很好的经典 MVC 流程图和关于组件交互的简单文章。

讨论示例

让我们尝试设计一个例子来理解这个问题。

假设我的应用程序中有一个用户列表。此列表可能显示在:

  • 主管理员列表视图
  • 管理员编辑用户视图
  • 用户的个人资料视图
  • 也许更多?

其中每个视图都将从模型图层请求数据并在屏幕上显示某些内容。

现在,假设更改已提交到一个用户的配置文件。这将通过控制器方法完成,该方法执行对模型应用某些更改所需的任何工作。

我的理解是,您希望更新所有这些视图以反映该更改。这意味着视图需要从模型中重新加载数据。它不应该从控制器本身获取此数据,即使控制器触发了此重新加载/刷新 - 或者更确切地说,控制器方法可能有助于从模型层进行查询。重要的部分是,您不会在整个应用程序的多个控制器中维护数据的多个副本。持久性集中在模型层中。

在 winforms 的情况下,模型层可能能够提供类似于上述 INotifyPropertyChanged 接口的内容,如果您的 UI 组件被构建为识别该接口并相应地刷新。但这是一种相当依赖于平台的方法。

一种与平台/上下文无关的方法将是已经提到的发布-订阅(发布-订阅)模式。在这种情况下,对模型进行更改的每个控制器方法也会发布其更改的通知。该数据的任何视图都可以通过从模型层刷新/重新加载视图的数据来侦听和响应此类通知。

我已经成功地使用了发布-订阅模式模式来解决此类问题。每当有人修改某些内容时,有人可能有兴趣知道,您都会引发"ObjectWasModified"事件,或者如果您想要"NewUserAdded"等更具体的内容......然后,每个子系统或其他需要知道何时发生这种情况的事情,它都会订阅该类型的事件。您可以将模板参数添加到事件对象被修改或其他条件,以便每个系统只接收它真正感兴趣的事件。

我知道WPF与MVVM的结合在很大程度上依赖于INotifyPropertyChanged和额外的INotifyCollectionChanged。也许这个简单的界面可能会为您解决问题。

在此处阅读有关此内容的信息。Josh Smith 关于 MVVM 基础知识的一篇文章,您可以在其中了解 INotifyPropertyChanged 的使用。虽然它在WPF环境中,但它也可以在WinForms中使用。

为什么不直接使用 .NET 中内置的事件模型? 我没正确理解这个问题吗? 您的问题表明您认为这增加了很多"开销"(不清楚您指的是性能还是开发),但从性能的角度来看,此解决方案非常轻量级。

class YourModel
{
    //---- event class can contain data elements to update listeners
    public class DataChangedEventArgs : EventArgs
    {
        ...
    }
    //---- this is what the client callback need to look like
    public delegate void DataChangedDelegate(object oSender, DataChangedEventArgs args);
    //---- public event that clients subscribe to
    public event DataChangedDelegate evtDataChanged;
    //---- any changes in YourModel invoke this method to notify clients
    protected void OnChanged(DataChangedEventArgs args)
    {
        if (evtDataChanged != null)
            evtDataChanged(this, args);
    }
    //---- method(s) in your Model that change internal data
    public void ImaDataChanger(...) 
    {
        //---- stuff that changes the data
        OnChanged(args);    //-- notify clients
    }
}
class UserSelectionView
{
    //---- the event callback
    public void DataChangedHandler(object oSender, YourModel.DataChangedEventArgs args)
    {
        //---- process update or refresh data
        //---- UI updates will have to be marshalled to the UI thread
    }
    //---- sign up for events
    public void Subscribe(YourModel model)
    {
        model.evtDataChanged += new YourModel.DataChangedDelegate(DataChangedHandler);
    }
}

您可以选择定义其他事件或将数据成员添加到 DataChangedEventArgs 以提供有关数据更改类型的信息。

此外,如下面的注释所示,如果需要将视图放在不同的计算机上或在同一台计算机上的不同进程中,则可以使用 .NET 远程处理来实现此目的,只需对代码进行很少的更改。