【问题标题】:ASP.NET - Separation of concernsASP.NET - 关注点分离
【发布时间】:2008-11-11 17:58:22
【问题描述】:

想象以下场景 - 我们的 Page1 包含控件 Control A 和 Control B。

假设控件 A 有一个按钮,单击此按钮时我们希望控件 B 做出反应。 但是我们想以抽象的方式来做这件事,即我们不能让 Control B 知道关于 Control A 的任何事情,反之亦然。

这样我们可以单独开发这些控件,并通过单元测试来驱动它们。

现在,我认为我有解决方案,只是想知道你们的想法。

在控件 A 的按钮单击时,我在 Session 上放置了一个“消息”,即 Session["MESSAGES"] = "ControlA_Click"。

在 Page1 中,在 Page_LoadComplete() 上,我调用了 ProcessMessages,如下所示:

            List<Message> messages = SessionMessages.GetMessageList(Page);
        foreach(Message m in messages)
        {
            //Get Controls
            ControlA controlA = FindControl("controlA") as ControlA;
            controlA .ProcessMessage(m);

            ControlB controlB = FindControl("controlB") as ControlB;
            controlB.ProcessMessage(m);
      }

在 ControlB 的 ProcessMessage() 方法中,我们可以对 ControlB 感兴趣的消息做出反应,如下所示:

    if (m.MessageName == SessionMessages.C_MESSAGE_SEARCH)
{
    this.Visible = true;
}

对我来说,这似乎可行。它允许我们完全独立地开发这些控件,同时仍然允许抽象级别的控件间通信。

我能想到的唯一可能导致崩溃的是也许与页面和用户控件相关的 ASP.NET 生命周期。我认为它的方式是所有事件都应该在拥有页面上调用 Page_LoadComplete() 之前在控件上处理。

想法?

【问题讨论】:

  • @Steven - 你说得对,我的意思是说我有点意识到我在这里有点滥用 Session ......我想我正在寻找“任何地方”我可以存储一些状态信息。但期待关于这整个概念是否会成功的意见!

标签: asp.net unit-testing mocking separation-of-concerns isolation


【解决方案1】:
  1. 控件 A 应引发事件
  2. 包含控件的页面订阅事件,然后在另一个控件中调用适当的方法
  3. 控件 B 应该处理 message()

【讨论】:

  • 我最初的想法是消除让页面充当“中间人”并直接连接您看到的事件/调用的需要。你的方法肯定会奏效,但它更硬一些,因此更难以进行单元测试。上船了!
【解决方案2】:

一个有趣的会话滥用......

您也可以让消息队列属于托管页面

我建议您改为让托管页面对控件执行某些操作以响应消息,而不是使控件变得“智能” - 实际上不需要按钮“智能”

【讨论】:

  • 是的,它不一定是 Session,欢迎提出建议!
  • 对不起!嗯,看到我想做的是将所有内容封装在控件中,因为我真的希望托管页面尽可能地愚蠢......认为我们在这一方面处于对立面,但意见赞赏并指出:)
  • @Duncan:告诉按钮做什么的逻辑(“控制器”模式)可以像你喜欢的那样通用和解耦,让所有用户界面对象(按钮和页面)'薄和愚蠢的';-)
  • 是的,我要使用 Supervising-Presenter 模式,不想把它放在这里,因为它可能会进一步复杂化。但是我仍然希望它位于实际控件本身的后面...但是您是说您将拥有一个 PageController 而不是 ControlAController 和 ControlBController?
  • @Duncan:是的,控制器是交互的桥梁(如果你的交互是模型驱动的,你可能会发现你需要完整的模型-视图-控制器模式);它可以使用消息、直接逻辑、属性和接口、事件或任何适合您情况的内容
【解决方案3】:

正如 Briggie 所暗示的 - 这正是 Model-View Presenter 的全部意义所在。这里是 .NET 中 MVP 的 an article,如果你想自己动手的话。

理想情况下,您希望将 MVC 框架作为一个示例,以说明将所有内容分离后可以做什么。

我通常做的是让按钮单击事件引发特定于域的事件,例如:


private void ControlA_OnClick(..)
{
  if(LoginRequested != null)
    LoginRequested(this, loginObj);
}

这样就清楚地说明了为什么有人会单击按钮并推动分离。

【讨论】:

    【解决方案4】:

    您所拥有的几乎是EventBroker。我不认为 Session 是合适的地方,因为没有必要跨越请求。 HttpContext 可能有效,但除非我希望在 IHttpModules 和 IHttpHandlers 之间共享消息总线,否则我可能只是使用自定义控件可以将其 Page 实例转换为的基 Page 类:

    interface IEventBroker {
     void Send(Message m);
    }
    
    class ControlA {
      void MyButton_Click(object sender, EventArgs e) {
         var eb = this.Page as IEventBroker;
         if (eb != null) eb.Send(new Message());
      }
    }
    

    或为控件提供对 EventBroker 的引用 - 在这种情况下,我可能会将 EventBroker 本身设置为控件并为每个控件提供 ID,以便它们可以使用 Page.FindControl。

    【讨论】:

    【解决方案5】:

    这不是数据绑定的用途吗?控件 A 响应更新模型的事件,然后对其依赖项调用 databind。

    如果你想做一个消息系统,把它设计成发布者和订阅者不需要互相了解,只需要消息本身。创建一个类似的接口:

    public interface IHandle<T> where T:IMessage
    {
         void Process(T message)
    }
    

    您将需要一种方法来发现哪些控件实现了它并构建消息类型->处理程序的映射,看看主要的 DI 框架处理属性注入到 ASP .NET 控件的方式,看看如何实现这一点。然后,您可以使用单个 SendMessage 方法,该方法负责将消息分派给可以处理该消息的所有控件。这种模式在表单 UI 中更为常见。

    【讨论】:

      【解决方案6】:

      查看Managed Extensibility Framework Contrib 项目。他们只有一个示例网站,正是您想要的。

      【讨论】:

        【解决方案7】:

        这种方法存在一些问题,例如:
        - 编译时未验证“事件”(您很容易输入错误的事件名称并在运行时或更糟糕的情况下找到相关信息)
        - 用交流的东西填充会话
        - 您需要将控件名称作为 字符串 - 如果有多个控件是这些事件的订阅者,则可能难以控制
        - 当需要在控件之间发送参数时,解决方案将变得更加难以管理

        更好的方法是通过在控件上声明事件来使用内置事件机制:

        public event EventHandler SpecialClick;
        

        每个需要做某事的控件都会订阅这个事件

        controlA.SpecialClick += new EventHandler(controlA_SpecialClick)
        

        使用正常的 dot.net 事件。

        【讨论】:

          猜你喜欢
          • 2015-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-20
          • 2016-05-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多