简体   繁体   English

ASP.NET-关注点分离

[英]ASP.NET - Separation of concerns

Imagine the following scenario - we have Page1 which contains controls Control A and Control B. 想象以下情况-我们有Page1,其中包含控件Control A和ControlB。

Say Control A has a button, and on the click of this button we want Control B to react. 假设控件A有一个按钮,然后单击该按钮,我们希望控件B做出反应。 But we want to do this in an abstract fashion, ie we can't have Control B knowing anything about Control A, and vice versa. 但是我们希望以抽象的方式进行操作,即我们不能让控件B知道有关控件A的任何信息,反之亦然。

That way we can develop these controls in isolation, and drive them by unit-testing. 这样,我们可以孤立地开发这些控件,并通过单元测试来驱动它们。

Now, I thought I had the solution, just want to know what you guys think of it. 现在,我以为我有解决方案,只想知道你们的想法。

On Control A's button click, I put a 'message' on the Session, ie Session["MESSAGES"] = "ControlA_Click". 在控件A的按钮单击上,我在会话上添加了“消息”,即Session [“ MESSAGES”] =“ ControlA_Click”。

In Page1, on the Page_LoadComplete(), I put a call to ProcessMessages, which looks like this: 在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);
      }

in ControlB's ProcessMessage() method, we can react to the messages that ControlB is interested in, like so: 在ControlB的ProcessMessage()方法中,我们可以对ControlB感兴趣的消息做出反应,如下所示:

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

To me, this seems to work. 对我来说,这似乎可行。 It allows us to develop these controls completely separately from eachother, while still allowing for inter-control-communication at an abstract level. 它使我们可以完全彼此独立地开发这些控件,同时仍然允许抽象级别的控件间通信。

The only thing I can think of that might bring this crashing down is perhaps the ASP.NET life-cycle in relation to Pages and User Controls. 我唯一想到的可能是崩溃的原因可能是与页面和用户控件有关的ASP.NET生命周期。 The way I figure it though is that ALL events should have been processed on the controls before Page_LoadComplete() is called on the owning Page. 我的想法是,在拥有的页面上调用Page_LoadComplete()之前,应该已经在控件上处理了所有事件。

Thoughts? 有什么想法吗?

  1. Control A should raise an event 控件A应该引发一个事件
  2. The page housing the controls subscribes to the event & then calls the appropriate method in the other control 包含控件的页面预订事件,然后在另一个控件中调用适当的方法
  3. Control B should process the message() 控件B应该处理message()

an interesting abuse of the session... 会议的一个有趣的滥用...

you could also have the message queue belong to the hosting page instead 您也可以让消息队列属于托管页面

i would recommend that instead you have the hosting page do something to the control in response to the message, rather than making the control be 'smart' - there is really no need for a button to be 'smart' 我建议您改为让托管页面对控件做出响应以响应消息,而不是使控件“智能”-确实不需要按钮“智能”

As Briggie alludes to - this is exactly what Model-View Presenter is all about. 正如Briggie所暗示的-这正是Model-View Presenter的目的所在。 Here's an article around MVP in .NET, if you want to roll your own. 如果您想自己动手制作,这是一篇有关.NET中MVP 的文章

Ideally you want to look at the MVC framework as an example of what you can do when you separate out everything. 理想情况下,您希望以MVC框架为例,说明分离所有内容时可以执行的操作。

What I normally do is have the button click event raise a domain-specific event, something like: 我通常要做的是让按钮单击事件引发特定于域的事件,例如:


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

That way it makes it clear why someone would click the button and drives home the separation. 这样一来,很清楚为什么有人会单击按钮并驱车回家。

Isn't this what databinding is for? 这不是数据绑定的目的吗? Control A responds to an event that updates the model and then calls databind on its dependencies. 控件A响应更新模型的事件,然后根据其依赖项调用databind。

If you want to make a messaging system, design it to the publisher and subscriber do not need to know about each other, only the message itself. 如果要创建消息传递系统,则设计给发布者和订阅者的消息不需要彼此了解,而只需了解消息本身即可。 Create an interface something like: 创建如下界面:

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

You will need a method of discovering which controls implement it and build a map of messagetype->handlers, have a look at the way the main DI frameworks handle property injection to ASP .NET controls to see how you can achieve this. 您将需要一种方法来发现实现该控件的控件并构建messagetype-> handlers的映射,并了解主要的DI框架处理对ASP .NET控件的属性注入的方式,以了解如何实现这一点。 You can then use a single SendMessage method which is responsible for dispatching the message to all controls that can handle that message. 然后,您可以使用一个SendMessage方法,该方法负责将消息调度到可以处理该消息的所有控件。 It's more common see this sort of pattern in forms UI. 更常见的是在表单UI中看到这种模式。

What you have is pretty much an EventBroker . 您拥有的几乎是一个EventBroker I don't think Session is the appropiate place for this, as it's not necessary to live across requests. 我认为Session不是适合此操作的地方,因为没有必要跨请求进行处理。 HttpContext might work, but unless I wanted the message bus to be shared between IHttpModules and IHttpHandlers, I'd probably just either use a base Page class that custom controls can cast their Page instance to: 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());
  }
}

or give the controls a reference to the EventBroker - in which case I'd probably make the EventBroker itself a control and give the ID to each control so that they could use Page.FindControl. 或为控件提供对EventBroker的引用-在这种情况下,我可能会让EventBroker本身成为控件,并为每个控件提供ID,以便它们可以使用Page.FindControl。

Check out the Managed Extensibility Framework Contrib project. 签出Managed Extensibility Framework Contrib项目。 They have just a sample website that is just what you want. 他们只有您想要的样本网站。

There are some problems with this approach like: 这种方法存在一些问题,例如:
- The 'events' are not verified at compile (you can easily mistype an event name and find out about this at runtime or worst) -编译时未验证“事件”(您可以很容易地输错事件名称,并在运行时或更糟的情况下查找有关事件的名称)
- Filling session with communication stuff -用通讯内容填充会话
- You need to put control names as strings - If there are more than one control that is a subscriber to these event can become difficult to control -您需要将控件名称作为字符串放置-如果多个控件是这些事件的订阅者,则可能难以控制
- When parameters will need to be send between controls the solution will become more difficult to manage -当需要在控件之间发送参数时,解决方案将变得更加难以管理

A better approach is to use the build in event mechanism by declaring events on the controls: 更好的方法是通过在控件上声明事件来使用内置事件机制:

public event EventHandler SpecialClick;

Each control that needs to do something will subscribe to this event 每个需要做某事的控件都将订阅此事件

controlA.SpecialClick += new EventHandler(controlA_SpecialClick)

using the normal dot.net events. 使用正常的dot.net事件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM