如何实现对上下文(Context)数据的统一管理 [提供源代码下载]
在应用开发中,我们经常需要设置一些上下文(Context)信息,这些上下文信息一般基于当前的会话(Session),比如当前登录用户的个人信息;或者基于当前方法调用栈,比如在同一个调用中涉及的多个层次之间数据。在这篇文章中,我创建了一个称为ApplicationContext的组件,对上下文信息进行统一的管理。[Source Code从这里下载] 一、基于CallContext和HttpSessionState的ApplicationContext如何实现对上下文信息的存储,对于Web应用来说,我们可以借助于HttpSessionState;对于GUI应用来讲,我们则可以使用CallConext。ApplicationContext完全是借助于这两者建立起来的,首先来看看其定义: 1: using System; 3: using System.Runtime.Remoting.Messaging;
5: namespace Artech.ApplicationContexts
7: [Serializable] 9: { 11: 13: { 15: { 17: { 19: { 21: } 23: return HttpContext.Current.Session[ContextKey] as ApplicationContext; 25:? 27: { 29: } 31: } 33: }
二、ApplicationContext在异步调用中的局限在同步调用的情况下,ApplicationContext可以正常工作。但是对于异步调用,当前的上下文信息并不能被传播到另一个线程中去。接下来,我们将给出一个简单的例子,模拟通过ApplicationContext存贮用户的Profile信息,为此,我定义了如下一个Profile类,属性FirstName、LastName和Age代表三个Profile属性。 3: {
5: class Profile
7: string FirstName
9: string LastName
11: int Age
13: public Profile()
15: this.FirstName = "N/A"; 17: this.Age = 0;
19: } 1: [Serializable] 5:? 7: { 9: { 11: { 13: } 15: } 17: } “Save”、“Clear”、“Get [Sync]”和“Get [Async]”响应的事件处理程序如下面的代码所示: namespace WindowsApp
partial class ProfileForm : System.Windows.Forms.Form
8: { 10: } private void buttonSave_Click(object sender,EventArgs e) 15: ApplicationContext.Current.Profile.LastName = this.textBoxLastName.Text.Trim();
17: } 19: void buttonClear_Click( 20: { 22: this.textBoxLastName.Text = string.Empty; 24: } 28: this.textBoxFirstName.Text = ApplicationContext.Current.Profile.FirstName;
30: this.numericUpDownAge.Value = ApplicationContext.Current.Profile.Age;
32:? 35: GetProfile getProfileDel = () => 37: return ApplicationContext.Current.Profile;
39: IAsyncResult asynResult = getProfileDel.BeginInvoke(null,1)">null);
41: this.textBoxFirstName.Text = profile.FirstName;
43: this.numericUpDownAge.Value = profile.Age;
45:? 47: } 1: [Serializable,ComVisible(true),SecurityPermission(SecurityAction.LinkDemand,Flags = SecurityPermissionFlag.Infrastructure)]
4: object GetData(string name);
object LogicalGetData( 9: void LogicalSetData(string name,1)">object data); 11: void SetHeaders(Header[] headers);
13: object HostContext { get; [SecurityPermission(SecurityAction.LinkDemand,Flags = SecurityPermissionFlag.Infrastructure)] set; }
//其他成员 7: get 12: CallContext.LogicalSetData(ContextKey,1)">as ApplicationContext; object>,ILogicalThreadAffinative 5: } 现在再次运行我们上面的Windows Form应用,点击“Get [Async]”按钮后将会得到正确的Profile显示,有兴趣的读者不妨下载实例代码试试。但是当运行Web应用的时候,依然有问题,为此我们需要进行一些额外工作。 五、通过ASP.NET扩展解决Web应用的异步调用问题在上面我们已经提过,ASP.NET管道将当前的HttpContext的存储与基于当前线程的CallContext中,而存贮的形式是IllogicalCallContext而非LogicalCallContext,说在非请求处理线程是获取不到当前HttpContext的。针对我们ApplicationContext就意味着:在Web应用中,主线程实际上操作的是当前HttpContext的Session,而另外一个线程中则是直接使用CallConext。 那么如果我们们能够将存储与当前HttpContext的Session中的ApplicationContext作为LogicalCallContext拷贝到CallContext中,那么在进行异步调用的时候,就能自动传递到另外一个线程之中了。此外,由于ASP.NET采用线程池的机制处理HTTP请求,我们需要将当前CallContext的数据进行及时清理,以免被另外一个请求复用。我们可以有很多方式实现这样的功能,比如在Global.asax中定义响应的事件处理方法,自定义HttpApplication或者自定义HttpModule。 如果自定义HttpModule,我们可以注册HttpApplication的两个事件:PostAcquireRequestState和PreSendRequestContent,分别实现对当前ApplicationContext的拷贝和清理。具体定义如下: 7:?
void Init(HttpApplication context) 11: context.PostAcquireRequestState += (sender,args) => 13: CallContext.SetData(ApplicationContext.ContextKey,ApplicationContext.Current); 15: context.PreSendRequestContent += (sender,1)" id="lnum16"> 16: { 19: } 21: } 我们只需要通过如下的配置将其应用到我们的程序之中即可: <configuration>
</ 7: > 相关内容
推荐文章
站长推荐
热点阅读
|