加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

如何实现对上下文(Context)数据的统一管理 [提供源代码下载]

发布时间:2020-12-16 09:09:38 所属栏目:asp.Net 来源:网络整理
导读:在应用开发中,我们经常需要设置一些上下文(Context)信息,这些上下文信息一般基于当前的会话(Session),比如当前登录用户的个人信息;或者基于当前方法调用栈,比如在同一个调用中涉及的多个层次之间数据。在这篇文章中,我创建了一个称为ApplicationCo

在应用开发中,我们经常需要设置一些上下文(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:     }
  
   2: ApplicationContext.Current["UserName"] = "Foo";
   4: var userName = ApplicationContext.Current["UserName"];

二、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: }

现在我们来看看ApplicationContext在一个简单的Windows Form应用中的使用情况。在如右图(点击看大图)所示的一个Form中,我们可以进行Profile的设置和获取。其中“Get [Sync]”和“Get [Async]”按钮分别模拟对存贮于当前ApplicationContext中的Profile信息进行同步异步方式的获取,通过点击Save按钮将设置的Profile信息保存到当前的ApplicationContext之中。

“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:   >