ASP.NET - 关注点分离

时间:2008-11-11 17:58:22

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

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

Say Control A有一个按钮,单击此按钮我们希望Control B做出反应。 但我们想以抽象的方式做到这一点,即我们不能让Control B知道关于Control A的任何信息,反之亦然。

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

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

在控制A的按钮上单击,我在会话上放了一条“消息”,即会话[“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()之前,应该已经在控件上处理了所有事件。

思想?

7 个答案:

答案 0 :(得分:6)

  1. 控件A应该举起活动
  2. 包含控件的页面订阅了事件&amp;然后在另一个控件中调用适当的方法
  3. 控制B应处理消息()

答案 1 :(得分:2)

有趣的滥用会议......

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

我建议你让主机页面对控件做一些响应消息的事情,而不是让控件变得“聪明” - 真的没有必要让按钮变得'聪明'

答案 2 :(得分:2)

正如Briggie所暗示的那样 - 这正是Model-View Presenter的全部内容。这里是an article围绕MV的MVP,如果你想自己动手。

理想情况下,您希望将MVC框架视为您在分离所有内容时可以执行的操作的示例。

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


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

这样就可以清楚地说明为什么有人会点击按钮并将分离带回家。

答案 3 :(得分:1)

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

如果要创建消息传递系统,请将其设计给发布者,订阅者不需要彼此了解,只需知道消息本身。创建一个类似于:

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

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

答案 4 :(得分:1)

你所拥有的几乎是EventBroker。我不认为Session是适合这种情况的地方,因为没有必要跨越请求。 HttpContext可能有效,但除非我希望在IHttpModules和IHttpHandlers之间共享消息总线,否则我可能只使用自定义控件可以将其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 :(得分:0)

查看Managed Extensibility Framework Contrib项目。他们只是一个样本网站,正是你想要的。

答案 6 :(得分:0)

这种方法存在一些问题,例如:
   - “事件”未在编译时验证(您可以轻松输错事件名称并在运行时或最差时查找此事件)
   - 与通信内容的填充会话
   - 您需要将控件名称设置为     字符串    - 如果有多个控件是这些事件的订户可能变得难以控制
   - 当需要在控件之间发送参数时,解决方案将变得更难以管理

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

public event EventHandler SpecialClick;

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

controlA.SpecialClick += new EventHandler(controlA_SpecialClick)

使用正常的dot.net事件。