设计:如何在应用程序中通知控制器数据修改的情况

14
在一个基于MVC的大型系统中,有一些负责编辑数据的视图以及一些展示数据的视图。
例如:UserManagementView和UserSelectionView。
每个子系统都应该知道在另一个子系统对相同数据进行更改后它是否需要数据更新,以便其控制器知道是否需要数据更新。
我认为这类似于观察者模式(在C#中已经集成了),所有控制器都将成为监听器,并最终会收到通知已发生数据操作。
例如,BindingList<>提供了ListChanged事件。类似地,可以为每个数据结构创建一个接口并通知控制器有关更改的信息。这会增加开销(在我看来),而且我认为在大型系统中难以维护,同时无论如何更新数据也不是解决方案。
哪种架构设计可以帮助在这种情况下进行处理?

控制器应该决定这个吗?为什么不将这个任务委托给业务类呢? - coder_bro
是的。实际上,我更感兴趣的是这种设计的架构。你说得对,控制器负责这个。问题仍然存在... - Odys
4个回答

5
这个问题听起来像是你试图在没有模型的情况下使用MVC。如果我误解了,编辑你的问题并包含一个实际用例(示例)可能会帮助我们理解上下文。
总的来说,你的控制器中不应该真正存储/持久化任何东西。因此,在控制器中不应该有任何需要“更新”或“通知”的内容(即:没有数据)。相反,数据应该在单独的“模型”层中,该层管理所有数据。然后,视图从模型层读取以获取该视图的任何数据。
为了快速温习,请查看wikipedia page on MVC,其中有一个很好的经典MVC流程图和关于组件交互的简单描述。
讨论的例子
让我们试着构造一个例子,以便更好地理解这个问题。
假设我在我的应用程序中有一个用户列表。这个列表可能显示在:
- 主管理员列表视图 - 管理员编辑用户视图 - 用户的个人资料视图 - 或者更多?
每个视图都将从模型层请求数据并在屏幕上显示某些内容。
现在假设对一个用户的个人资料进行了更改。这将通过控制器方法完成,该方法将执行必要的工作以应用一些更改到模型中。
我的理解是您希望所有这些视图都更新以反映该更改。这意味着视图需要重新加载来自模型的数据。它不应该直接从控制器获取此数据,即使控制器触发此重新加载/刷新-或者控制器方法可以促进从模型层查询。重要的是,您不会在应用程序中的多个控制器中维护多个数据副本。持久性集中在模型层中。
在winforms的情况下,如果您的UI组件构建为识别INotifyPropertyChanged接口并相应地刷新,则模型层可能能够提供类似于所述接口的内容。但是,这是一种相当平台依赖的方法。
更具平台/上下文无关性的方法是已经提到的发布-订阅(pub-sub)模式。在这种情况下,对模型进行更改的每个控制器方法也会发布其更改的通知。任何与该数据相关的视图都可以监听并响应此类通知,通过从模型层刷新/重新加载视图的数据。

3
我知道WPF结合MVVM需要大量使用INotifyPropertyChangedINotifyCollectionChanged。也许这个简单的接口对你有用。

在这里阅读Josh Smith关于MVVM基础的文章,你可以理解INotifyPropertyChanged的使用。虽然它是在WPF环境下的,但在WinForms中也可以使用。


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

3
为什么不使用.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 Remoting来实现这一点,几乎不需要更改代码。

1
这是一个有趣的问题,因为它是一个具有特定用例的抽象问题。.Net事件模型对于基于Web的MVC应用程序不起作用,因为视图的生命周期很短,直到它们被呈现。除非您设置了一些发布-订阅系统来执行此操作,否则无法从.Net事件强制刷新Web页面。但是,您的建议可能适用于单台计算机上的WinForms应用程序(这就是OP所问的,我只是更加学术 :-)。 - BenSwayne
它明确指出了WinForms。我不会建议在ASP中使用这个解决方案。此外,你关于这只适用于单台计算机的说法是错误的。使用.NET Remoting,这个解决方案可以很容易地扩展到单台计算机上的多个进程或在不同计算机上运行的多个客户端。只是为了告知学术辩论... - Kent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接