在ASP与ASP.NET之间共享对话状态

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介: [前言:] ASP.NET是微软提供的最新的开发基于Web的应用程序的技术。它提供了大量的比传统ASP脚本技术的好处,包括:  1)通过把UI表现层(presentation)与商业逻辑(business logic)分开建立了更好的开发结构;  2)使用完全编译的代码代替了传统ASP的代码翻译;  3)它编译特性与每个支持的方法协同,这意味着使用ASP.NET的站点比使用传统的ASP站点的性能更高。

[前言:]

ASP.NET是微软提供的最新的开发基于Web的应用程序的技术。它提供了大量的比传统ASP脚本技术的好处,包括:
  1)通过把UI表现层(presentation)与商业逻辑(business logic)分开建立了更好的开发结构;
  2)使用完全编译的代码代替了传统ASP的代码翻译;
  3)它编译特性与每个支持的方法协同,这意味着使用ASP.NET的站点比使用传统的ASP站点的性能更高。
  尽管把存在的ASP应用程序转换到ASP.NET有很多潜在的好处,也有些ASP应用程序任务很重要并且复杂。转换过程可能需要更多资源并给应用程序带来更多风险。解决这些问题的途径之一是同时运行ASP和ASP.NET应用程序,在一个时刻将一个对话转换为ASP.NET。为了同时运行新旧程序,需要建立一个机制在传统的ASP与ASP.NET间共享对话状态。本文讨论的是怎样使用.NET框架组件的类和序列化特性共享状态信息。

  概述
  Cookie是Web应用程序识别用户对话的最常用的方法,可以用于识别传统的ASP和ASP.NET对话状态。在ASP脚本中状态信息保存在内存中,不能与其它应用程序(例如ASP.NET)共享。如果对话状态使用通用格式保存在微软SQL Server中,它就可以被传统的ASP和ASP.NET共同访问。
  在本例中,名为mySession的Cookie用于识别用户对话。当用户对Web应用程序作出请求时,将为该用户产生唯一的Cookie用于识别对话。在随后的请求中,浏览器将该唯一的Cookie发送回服务器用来识别对话。在被请求的Web页载入前,一个自定义对象将使用该唯一的Cookie从SQL Server中重新载入用户对话数据。通过自定义对象Web页中的对话状态是可以访问的。Web请求完成后,如果请求终止,对话数据将保存回SQL Server(见图1)。

 

图1.数据流简单图

  ASP.NET实现
  在ASP.NET中每一个Web页都衍生自System.Web.UI.Page类。Page类集合了HttpSession对象的一个实例用于处理对话数据。在本例中,叫做SessionPage的自定义Page类来衍生自System.Web.UI.Page,提供了类似Page类的所有特性。唯一的区别是默认的HttpSession使用自定义的对话对象重载了(对实例变量使用new修改符,C#允许衍生的类隐藏基类的成员)。

1 public class SessionPage : System.Web.UI.Page 
2 {  
3     ...  
4     public new mySession Session = null; 
5    ... 
6 }

 

  自定义的对话类使用HybridDictionary对象来相应保存内存中的对话状态(HybridDictionary可用于处理任意数量的对话元素)。为了与传统的ASP通用,该自定义对话对象将限制对话数据类型为字符串型(默认的HttpSession允许对话保存任意类型的数据,不能与传统的ASP通用)。

 1 [Serializable] 
 2 public class mySession 
 3 {  
 4     private HybridDictionary dic = new HybridDictionary();
 5 
 6     public mySession()  {  }
 7 
 8    public string this [string name]  
 9     {   
10         get   
11             {    
12                 return (string)dic[name.ToLower()];   
13             }   
14         set   
15             {    
16                 dic[name.ToLower()] = value;   
17             }  
18     } 
19 } 

 

  Page类为定制暴露了不同的事件和方法。特别是OnInit方法用于设置Page对象的初始化状态。如果请求不包含名为mySession的Cookie,将为请求者产生一个新的mySession Cookie。另外,对话数据将使用自定义数据访问对象SessionPersistence从SQL Server中检索出来。DSN和SessionExpiration的值从web.config中检索。

override protected void OnInit(EventArgs e) 
{
  InitializeComponent();
  base.OnInit(e);
 }
 private void InitializeComponent() {

  cookie = this.Request.Cookies[sessionPersistence.SessionID];

 if (cookie == null) 
 {
   Session = new mySession();
   CreateNewSessionCookie();
   IsNewSession = true;
  }
  else
   Session = sessionPersistence.LoadSession(
     Server.UrlDecode(cookie.Value).ToLower().Trim(), 
    dsn,
     SessionExpiration
    );
  this.Unload += new EventHandler(this.PersistSession);
 }
 private void CreateNewSessionCookie() {
  cookie = new HttpCookie(sessionPersistence.SessionID, 
  sessionPersistence.GenerateKey());
  this.Response.Cookies.Add(cookie);
 }

 

  SessionPersistence类使用微软.NET框架组件的BinaryFormatter来串行化和并行化对话状态为二进制格式以提供最佳性能。结果的二进制对话数据接着作为图象字段类型被存入SQL Server。

public mySession LoadSession(string key, string dsn, int SessionExpiration) {
  SqlConnection conn = new SqlConnection(dsn);
  SqlCommand LoadCmd = new SqlCommand();
    LoadCmd.CommandText = command;
  LoadCmd.Connection = conn;
  SqlDataReader reader = null;
  mySession Session = null;

 try
  {
   LoadCmd.Parameters.Add("@ID", new Guid(key));
   conn.Open(); 
  reader = LoadCmd.ExecuteReader();
   if (reader.Read()) 
  {
    DateTime LastAccessed =reader.GetDateTime(1).AddMinutes(SessionExpiration);
    if (LastAccessed >= DateTime.Now)
     Session = Deserialize((Byte[])reader["Data"]);
   }
  }
  finally
  {
   if (reader != null)
    reader.Close();
   if (conn != null) 
   conn.Close(); 
 }
 return Session;
 }
 private mySession Deserialize(Byte[] state) { 
 if (state == null) return null;
 mySession Session = null;
  Stream stream = null;

 try
  {
   stream = new MemoryStream();
   stream.Write(state, 0, state.Length);
   stream.Position = 0;
   IFormatter formatter = new BinaryFormatter();
   Session = (mySession)formatter.Deserialize(stream);
  }
  finally
  {
   if (stream != null)
    stream.Close();
  }
  return Session;
 }

 

  在请求的末尾,Page类的Unload事件被启动了,一个同Unload事件一起注册的事件处理方法将串行化对话数据为二进制格式并将结果二进制数据存入SQL Server。

 1 private void PersistSession(Object obj, System.EventArgs arg) { sessionPersistence.SaveSession(
 2    Server.UrlDecode(cookie.Value).ToLower().Trim(), dsn, Session, IsNewSession);
 3  }
 4  public void SaveSession(string key, string dsn, mySession Session, bool IsNewSession) {
 5   SqlConnection conn = new SqlConnection(dsn);
 6   SqlCommand SaveCmd = new SqlCommand(); 
 7   SaveCmd.Connection = conn;
 8  try
 9   {
10    if (IsNewSession)
11     SaveCmd.CommandText = InsertStatement;
12    else
13     SaveCmd.CommandText = UpdateStatement;
14 
15   SaveCmd.Parameters.Add("@ID", new Guid(key));
16       SaveCmd.Parameters.Add("@Data", Serialize(Session));             
17     SaveCmd.Parameters.Add("@LastAccessed", DateTime.Now.ToString());
18   conn.Open();
19    SaveCmd.ExecuteNonQuery();
20   }
21   finally
22   {
23    if (conn != null)
24     conn.Close(); 
25  }
26  }
27  private Byte[] Serialize(mySession Session) {
28   if (Session == null) return null;
29      Stream stream = null; 
30    Byte[] state = null;
31 
32  try
33   {
34    IFormatter formatter = new BinaryFormatter();
35    stream = new MemoryStream();
36    formatter.Serialize(stream, Session);
37    state = new Byte[stream.Length];
38    stream.Position = 0;
39    stream.Read(state, 0, (int)stream.Length);
40    stream.Close();
41   }
42   finally
43   {
44    if (stream != null)
45     stream.Close();
46   }
47   return state;
48  }

 

  SessionPage类以及与它相关的类被封装在SessionUtility组件中。在一个新ASP.NET项目中,需要作SessionUtility组件的引用,为了与传统的ASP代码共享对话,每个页面由SessionPage代替Page类衍生出来。一旦移植完成了,新应用程序能通过说明SessionPage类中定义的对话变量切换回使用原来的HttpSession对象来显示基本的HttpSession。

ASP实现
  原来的ASP对话只能将对话数据保存在内存中。为了将对话数据保存到SQL Server,需要写一个自定义的Visual Basic 6.0 COM对象代替现在的对话对象来管理对话状态。该COM对象在每个Web请求开始时被初始化,并从SQL Server重新载入对话数据。ASP脚本完成时,该对象将终止并把对话状态将返回到SQL Server。 Visual Basic 6 COM Session对象的主要目的是提供对微软Internet信息服务器(IIS)内部对象的访问。Visual Basic 6 COM对话对象使用SessionUtility组件的mySession类来保存对话状态,SessionUtility的SessionPersistence类用于载入和保存对话数据到SQL Server。使用regasm.exe工具将mySession和 SessionPersistence类作为COM对象暴露。regasm.exe工具可以注册并为COM客户端建立一个类型库来使用框架组件类。
  在对象的构造函数中状态信息被重新载入。构造函数(class_initialize)首先从Application对象中检索对话cookie、对话超时设置(SessionTimeOut)、数据库连接字符串(SessionDSN),并建立mySession类的一个实例来保持对话数据。接着构造函数将试图重新使用给定的cookie从SQL Server中载入对话数据。如果SQL Server中没有对话信息,或者对话已经终止,将产生一个新的cookie。如果SQL Server返回了对话状态数据,对话状态信息将保存在mySession对象中。

 1 Private Sub Class_Initialize()
 2   On Error GoTo ErrHandler:
 3   Const METHOD_NAME As String = "Class_Initialize"
 4   Set mySessionPersistence = New SessionPersistence
 5   Set myObjectContext = GetObjectContext()
 6   mySessionID = ReadSessionID()
 7   myDSNString = GetConnectionDSN()
 8   myTimeOut = GetSessionTimeOut()
 9   myIsNewSession = False
10   Call InitContents
11  Exit Sub ErrHandler:
12   Err.Raise Err.Number, METHOD_NAME & ":" & Err.Source, Err.Description End Sub
13 
14 Private Sub InitContents()
15   On Error GoTo ErrHandler:
16   Const METHOD_NAME As String = "InitContents"
17   If mySessionID = "" Then
18    Set myContentsEntity = New mySession
19    mySessionID = mySessionPersistence.GenerateKey   myIsNewSession = True
20   Else
21    Set myContentsEntity =mySessionPersistence.LoadSession(mySessionID, myDSNString,
22    myTimeOut)
23  End If
24  Exit Sub ErrHandler:
25   Err.Raise Err.Number, METHOD_NAME & ":" & Err.Source, Err.Description End Sub 

 

 

  如果对象实例超出了脚本的范围,将执行解构函数(class_terminate)。解构函数将使用SessionPersistence.SaveSession()方法保持对话数据。如果是一个新对话,解构函数将新cookie发送回浏览器。

Private Sub Class_Terminate()
  On Error GoTo ErrHandler:
  Const METHOD_NAME As String = "Class_Terminate"
  Call SetDataForSessionID
  Exit Sub ErrHandler:
  Err.Raise Err.Number, METHOD_NAME & ":" & Err.Source,Err.Description
 End Sub

Private Sub SetDataForSessionID()
  On Error GoTo ErrHandler:
  Const METHOD_NAME As String = "SetDataForSessionID"
      Call mySessionPersistence.SaveSession(mySessionID,      myDSNString, myContentsEntity, myIsNewSession)
 If myIsNewSession
 Then Call WriteSessionID(mySessionID)
 Set myContentsEntity = Nothing
  Set myObjectContext = Nothing
  Set mySessionPersistence = Nothing
  Exit Sub ErrHandler:
  Err.Raise Err.Number, METHOD_NAME & ":" & Err.Source,Err.Description
 End Sub

 

  例程
  例程设计为增加并显示一个数字。不管载入了哪个页面,由于数字值保存在SQL Server中并在ASP和ASP.NET间共享,数字将不断增加。
  建立例程的步骤
  1. 建立一个新数据库SessionDemoDb。
  2. 建立新表SessState(osql.exe -E -d SessionDemoDb -i Session.sql)。
  3. 建立新虚拟目录Demo。
  4. 在ASP配置页中关闭ASP对话。
  5. 将web.config、testPage.aspx、Global.asa、testPage.asp和GlobalInclude.asp复制到虚拟目录。
  6. 更新Global.asa和web.config中的DSN字符串设置。对话超时设置是可选的,默认为20分钟。
  7. 将SessionUtility.dll安装到全局组件缓存(gacutil /i SessionUtility.dll)。
  8. 使用regasm.exe把SessionUtility.dll作为COM对象暴露(regasm.exe SessionUtility.dll /tlb:SessionUtility.tlb)。
  9. 将SessionManager.dll复制到本地目录并使用regsvr32.exe注册(regsvr32 SessionManager.dll)。
  10. 赋予IUSR_<计算机名>帐户读取和运行访问SessionMgr.dll的权限。
  运行例程的步骤
  1. 打开微软Internet Explorer。
  2. 为传统的ASP中载入testPage.asp。数字"1"将出现在Web页中。
  3. 点击Internet Explorer的刷新来重新载入该页面。数字将增加。
  4. 为ASP.NET改变URL为testPage.aspx。数字仍然增加。
  5. 如果使用testPage.aspx开始,过程相同。

  在存在的ASP应用程序中插入COM对象
  开发ASP应用程序的一个通常的习惯是在每个脚本开始时包含一个文件来共享代码和常量。插入自定义对话对象的最后途径是在公有包含文件中加入示例代码。最后一步简单地使用自定义对话变量名代替了所有到对话对象的引用。

  限制/改进
  本方案不支持在Session对象中保存了COM对象的ASP应用程序。在这种情况下,为了使用自定义对话对象,需要一个自定义的调度器来串行化/并行化状态。此外,本方案也不支持保存字符串类型数组。做一些附加工作可以实现该特性:使用Visual Basic 6.0的Join函数在保存到对话对象前将数组元素连接成一个字符串,反向操作可以使用Visual Basic 6.0的Split函数将字符串分解为独立的数组元素。在.NET框架组件中,Join和Split方法使String类的成员。

  结论
  ASP.NET提出了新的编程范例和体系结构,并提供了许多比传统ASP的好处。尽管将ASP移植到ASP.NET的过程不简单,ASP.NET更好的编程模型和更高的性能将使转换过程值得。除了在Session对象中保存COM对象的特例,本文讨论的途径提供了一个简单实现迁移的解决方案。

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
6月前
|
存储 开发框架 前端开发
asp.net与asp.net优缺点及示例
asp.net与asp.net优缺点及示例
|
1月前
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
29 0
|
1月前
|
开发框架 中间件 .NET
C# .NET面试系列七:ASP.NET Core
## 第一部分:ASP.NET Core #### 1. 如何在 controller 中注入 service? 在.NET中,在ASP.NET Core应用程序中的Controller中注入服务通常使用<u>依赖注入(Dependency Injection)</u>来实现。以下是一些步骤,说明如何在Controller中注入服务: 1、创建服务 首先,确保你已经在应用程序中注册了服务。这通常在Startup.cs文件的ConfigureServices方法中完成。例如: ```c# services.AddScoped<IMyService, MyService>(); //
63 0
|
1月前
|
开发框架 前端开发 .NET
C# .NET面试系列六:ASP.NET MVC
<h2>ASP.NET MVC #### 1. MVC 中的 TempData\ViewBag\ViewData 区别? 在ASP.NET MVC中,TempData、ViewBag 和 ViewData 都是用于在控制器和视图之间传递数据的机制,但它们有一些区别。 <b>TempData:</b> 1、生命周期 ```c# TempData 的生命周期是短暂的,数据只在当前请求和下一次请求之间有效。一旦数据被读取,它就会被标记为已读,下一次请求时就会被清除。 ``` 2、用途 ```c# 主要用于在两个动作之间传递数据,例如在一个动作中设置 TempData,然后在重定向到另
99 5
|
3月前
|
开发框架 安全 搜索推荐
分享105个NET源码ASP源码,总有一款适合您
分享105个NET源码ASP源码,总有一款适合您
27 4
|
7月前
|
开发框架 前端开发 .NET
.NET 8 Release Candidate 1 (RC1)现已发布,包括许多针对ASP.NET Core的重要改进!
.NET 8 Release Candidate 1 (RC1)现已发布,包括许多针对ASP.NET Core的重要改进!
176 0
.NET 8 Release Candidate 1 (RC1)现已发布,包括许多针对ASP.NET Core的重要改进!
|
9月前
|
开发框架 前端开发 JavaScript
ASP .Net Core 中间件的使用(一):搭建静态文件服务器/访问指定文件
ASP .Net Core 中间件的使用(一):搭建静态文件服务器/访问指定文件
|
10月前
|
开发框架 供应链 前端开发
net基于asp.net的社区团购网站
社区团购系统依托社区团购系统和社区门店,是现在的一个重大市场和发展方向,通过研究企业在社区团购系统环境下的营销模式创新,对于普通的零售业和传统社区团购系统的转型发展具有重要的理论意义。随着互联网行业的发展,人们的生活方式发生着重大变化,人们越来越倾向于网络购物,这对传统企业来说如何把客户留下是一个重大挑战。就现在而言,由于社区团购的竞争已经进入最紧张激烈的阶段,有些团购平台甚至已经彼此之间打起了价格战,其中不乏有平台因为利润变少或资金链断裂而半途败亡。企业在实际的商业活动中,往往会面临许多等待优化的问题。因此,要在竞争激烈的市场中拔得头筹,必须重视提升对新商业模式的全面认知,科学于实际贴合的分
|
11月前
|
开发框架 .NET Apache
301重定向代码合集(iis,asp,php,asp.net,apache)
301重定向代码合集(iis,asp,php,asp.net,apache)
274 0
|
开发框架 前端开发 .NET
进入ASP .net mvc的世界
进入ASP .net mvc的世界
86 0