我想在我的ASP.NET Web Forms
应用程序中跟踪一些用户活动。例如,我将使用方法protected void btnSearch_Click(object sender, EventArgs args)
,因为它代表了我的更多业务需求和编程问题。
只是为了说清楚。我想要实现的是,当btnSearch_Click
被执行以对数据库表进行INSERT
时,该数据库表包含一些基本信息,例如执行了哪些确切的操作,何时执行,谁执行了它以及什么是搜索词。我想要跟踪很多动作,因此表格的结构仍有待解决,但这是一般的想法。
我的第一个想法是使用一些静态类,如:
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);
}
}
}
和
class Observer
{
public void HandleEvent(object sender, EventArgs args)
{
Console.WriteLine("Something happened to " + sender);
}
}
以及显示其工作原理的具体例子是:
static void Main(string[] args)
{
Observable observable = new Observable();
Observer observer = new Observer();
observable.SomethingHappened += observer.HandleEvent;
observable.DoSomething();
}
也许这包含足够的信息来实现Web表单的这种模式,但是我无法在Web Forms
的cotntext中检测到不同的角色。
我希望有一个地方可以注册我要跟踪的所有事件,如下所示:
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_Click
事件处理程序,我想将搜索字符串作为参数传递。
答案 0 :(得分:2)
在这种情况下,您不必自己实施观察者模式; ASP.NET管道已经以Application对象的回调形式为您实现了它;在这种特殊情况下,PostAuthorizeRequest回调应该可以很好地处理您的需求。
首先,WebForms管道的快速侧边栏:WebForms中处理UI交互的方式是通过名为PostBacks的抽象。它看起来好像是在处理从.aspx / .ascx控件中引发的事件,而实际上发生的事情是将请求(包含特殊命名字段的POST)发送到Web服务器。创建了一个新的页面实例来处理该请求,WebForms基础结构检测到它是一个PostBack,并调用事件处理程序。 (有关更多信息,请参阅the MSDN docs)
这对你意味着什么?好吧,如果你在Global.asax(或代码隐藏Global.asax.cs)中挂钩你的应用程序的PostAuthorizeRequest回调,你可以在识别用户并且他们被授予执行请求的授权后检查请求。特别是,您要注意三件事:首先,这是什么类型的请求?如果它不是POST,它不是PostBack,你可以在这里停止自定义处理。第二,请求的实际路径 - 这是用户正在与之交互的WebForms页面;如果它不是您对审核事件感兴趣的页面,请在此处停止。最后,PostBack的目标是什么?这可以通过检查HttpContext.Request.Form [" __ 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.
}