top
Loading...
在ASP與ASP.NET之間共享對話狀態

[前言:]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#允許衍生的類隱藏基類的成員)。

public class SessionPage : System.Web.UI.Page
{
...
public new mySession Session = null;
...
}

自定義的對話類使用HybridDictionary對象來相應保存內存中的對話狀態(HybridDictionary可用于處理任意數量的對話元素)。為了與傳統的ASP通用,該自定義對話對象將限制對話數據類型為字符串型(默認的HttpSession允許對話保存任意類型的數據,不能與傳統的ASP通用)。

[Serializable]
public class mySession
{
private HybridDictionary dic = new HybridDictionary();

public mySession()
{
}

public string this [string name]
{
get
{
return (string)dic[name.ToLower()];
}
set
{
dic[name.ToLower()] = value;
}
}
}

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。

private void PersistSession(Object obj, System.EventArgs arg)
{ sessionPersistence.SaveSession(
Server.UrlDecode(cookie.Value).ToLower().Trim(), dsn, Session, IsNewSession);
}
public void SaveSession(string key, string dsn,
mySession Session, bool IsNewSession)
{
SqlConnection conn = new SqlConnection(dsn);
SqlCommand SaveCmd = new SqlCommand();
SaveCmd.Connection = conn;

try
{
if (IsNewSession)
SaveCmd.CommandText = InsertStatement;
else
SaveCmd.CommandText = UpdateStatement;

SaveCmd.Parameters.Add("@ID", new Guid(key));
SaveCmd.Parameters.Add("@Data", Serialize(Session));
SaveCmd.Parameters.Add("@LastAccessed", DateTime.Now.ToString());

conn.Open();
SaveCmd.ExecuteNonQuery();
}
finally
{
if (conn != null)
conn.Close();
}
}
private Byte[] Serialize(mySession Session)
{
if (Session == null) return null;

Stream stream = null;
Byte[] state = null;

try
{
IFormatter formatter = new BinaryFormatter();
stream = new MemoryStream();
formatter.Serialize(stream, Session);
state = new Byte[stream.Length];
stream.Position = 0;
stream.Read(state, 0, (int)stream.Length);
stream.Close();
}
finally
{
if (stream != null)
stream.Close();
}
return state;
}

SessionPage類以及與它相關的類被封裝在SessionUtility組件中。在一個新ASP.NET項目中,需要作SessionUtility組件的引用,為了與傳統的ASP代碼共享對話,每個頁面由SessionPage代替Page類衍生出來。一旦移植完成了,新應用程序能通過說明SessionPage類中定義的對話變量切換回使用原來的HttpSession對象來顯示基本的HttpSession。

作者:http://www.zhujiangroad.com
來源:http://www.zhujiangroad.com
北斗有巢氏 有巢氏北斗