C# 基于观察者模式在asp.net Web窗体中实现审核跟踪

C# 基于观察者模式在asp.net Web窗体中实现审核跟踪,c#,delegates,observer-pattern,C#,Delegates,Observer Pattern,我想跟踪我的ASP.NET Web表单应用程序中的一些用户活动。例如,我将使用方法protectedvoidbtnsearch\u Click(objectsender,EventArgs args),因为它代表了我的更多业务需求和编程问题 只是想说清楚一点。我想要实现的是,当执行btnSearch_单击时,向数据库表插入一个插入,该数据库表包含一些基本信息,例如执行了哪些确切操作、何时执行、谁执行了该操作以及搜索词是什么。我将有很多行动,我想保持跟踪,所以表的结构仍有待决定,但这是一般的想法

我想跟踪我的
ASP.NET Web表单
应用程序中的一些用户活动。例如,我将使用方法
protectedvoidbtnsearch\u Click(objectsender,EventArgs args)
,因为它代表了我的更多业务需求和编程问题

只是想说清楚一点。我想要实现的是,当执行
btnSearch_单击
时,向数据库表插入一个
插入
,该数据库表包含一些基本信息,例如执行了哪些确切操作、何时执行、谁执行了该操作以及搜索词是什么。我将有很多行动,我想保持跟踪,所以表的结构仍有待决定,但这是一般的想法

我的第一个想法是使用一些静态类,如:

public static class UserHistory
{
  public static void Log(string methodName, string user, string message)
  {
    //execute SQL INSERT
  }
}
然后在事件处理程序中,我需要跟踪历史记录,只需添加:

UserHistory.Log("btnSearch_Click", "SomeUser", "The user searched for cats");
事实上,这对我来说已经是可以接受的了,并不是我不喜欢这种方法,而是首先-这是非常常见的任务,这种任务通常已经有一些模式可以帮助你以最好的方式实现逻辑,其次,我不是很有经验,我在这里看到了通过做一些事情来扩展我的知识的机会,也许在这个特殊的情况下,这些事情不是有益的,但无论如何都是好的

这就引出了
Observer
模式。老实说,和代表们一起工作对我来说总是很麻烦,但我也在努力学习,我知道这很重要,可以证明你为解决一些问题付出了自己的努力,所以我将解释我的理解,我应该使用这种模式来做这件事,但我希望得到一些我能理解的完整答案

我发现的简单示例是来自Jon Skeet的答案,他在那里实现了两个类:

 class Observable
    {
        public event EventHandler SomethingHappened;

        public void DoSomething()
        {
            EventHandler handler = SomethingHappened;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }

以及混凝土exmaple,以展示其工作原理:

    static void Main(string[] args)
    {
        Observable observable = new Observable();
        Observer observer = new Observer();
        observable.SomethingHappened += observer.HandleEvent;

        observable.DoSomething();

    }
也许这包含了足够的信息来实现web表单的这种模式,但是我无法在
web表单的上下文中检测到不同的角色

我所期望的是有一个地方可以注册我想要跟踪的所有事件,比如:

    protected void Page_Load(object sender, EventArgs e)
    {
        observable.SomethingHappened += btnSearch_Click;
        observable.SomethingHappened += btnLogin_Click;
    }
但如果我像这样尝试,它实际上什么也没做。我甚至不进去:

        public void DoSomething()
        {
            EventHandler handler = SomethingHappened;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

这是合乎逻辑的,但是如果我必须从每个方法显式地调用这个methid,而我想记录一些东西,那么使用一个简单的旧静态方法有什么区别呢?另外,我想传递一个参数(字符串消息),我也不知道该怎么做。因此,很明显,我对如何与代表/活动合作的理解很差,但如果有人帮助我实现这一点,我将不胜感激。例如,可以使用
btnSearch\u Click
事件处理程序,我希望将搜索字符串作为参数传递给它。

在这种情况下,您不必自己实现观察者模式;ASP.NET管道已经以回调应用程序对象的形式为您实现了它;在这种特殊情况下,PostAuthorizerRequest回调应该能够很好地处理您的需求

首先,WebForms管道上的快速侧栏:WebForms中处理UI交互的方式是通过一个称为回发的抽象。在您看来,好像您正在处理从.aspx/.ascx控件引发的事件,而实际上发生的是一个请求(包含特殊命名字段的帖子)正在发送到Web服务器。创建页面的一个新实例来处理该请求,WebForms基础结构检测到它是回发,并调用事件处理程序。(有关更多信息,请参阅)

这对你意味着什么?好的,如果您在Global.asax内(或在codebhind Global.asax.cs中)为您的应用程序挂接PostAuthorizerRequest回调,那么您可以在识别用户并授予他们执行请求的授权后检查请求。特别是,您需要注意三件事:第一,这是什么类型的请求?如果不是POST,也不是回发,您可以在此处停止自定义处理。第二,请求的实际路径-这是用户与之交互的WebForms页面;如果您对审核事件不感兴趣,请到此为止。最后,回发的目标是什么?这可以通过检查HttpContext.Request.Form[“\uuu EVENTTARGET”]的值来确定。如果它是您感兴趣的控件的名称,您现在可以记录交互

这在实践中会是什么样子?假设您有一个元组查找表,它将页面+控件名映射到消息。在我的示例中,我将使用IDictionary变量auditMap,为了简单起见,我将使用页面路径和控件名称的串联(例如:“MyPage.aspx::btnSearch”)来执行审核消息的查找:

void Application_PostAuthorizeRequest(object sender, EventArgs e)
{
    if (HttpContext.Current.Request.HttpMethod != "POST") return;

    String key = String.Concat(HttpContext.Current.Request.Path, "::", HttpContext.Current.Request.Form["__EVENTTARGET"]);
    String message;
    if (!auditMap.TryGetValue(key, out message) return;

    auditService.LogEvent(message);
    // Can also pass HttpContext.Current.User.Identity.Name into a format string, etc.
}

谢谢。这个主意很有趣。
Application\u postauthorizereRequest
是一个全局事件,我可以在哪里实现它,还是我必须为每个页面编写它?是;您可以将其添加到Global.asax。对不起,我应该提一下;我会修改答案的。对不起,还有一个问题。我喜欢这个想法,因为它将我的代码保存在一个地方,但是我将使用
Active Directory
,也许(还不确定)
formsAuthenticationTicker
来跟踪,您的建议与用户的关系已经确定。这是否意味着我应该使用某种会员资格提供者来利用你的建议?ASP.NET在将身份验证和页面处理分离开来方面做得相当好;通过处理此回调,如果请求是针对需要身份验证的页面和/或用户已通过身份验证,则将调用应用程序配置的身份验证机制(无论是Windows Integrated.Forms Auth还是其他身份验证)
void Application_PostAuthorizeRequest(object sender, EventArgs e)
{
    if (HttpContext.Current.Request.HttpMethod != "POST") return;

    String key = String.Concat(HttpContext.Current.Request.Path, "::", HttpContext.Current.Request.Form["__EVENTTARGET"]);
    String message;
    if (!auditMap.TryGetValue(key, out message) return;

    auditService.LogEvent(message);
    // Can also pass HttpContext.Current.User.Identity.Name into a format string, etc.
}