直接使用字串訪問會話字典的方式有幾個缺點:
1、很容易由於字串拼錯產生錯誤;
2、擷取的對象是object類型的,需要轉換到實際類型
好一點的方式是實現編寫一個類,封裝成屬性來使用,比如:
http://www.codeproject.com/KB/aspnet/typedsessionstate.aspx
其實可以使用BuildProvider+CodeDom來自動產生這個封裝代碼(類似Profile的原理)
先來實現BuildProvider:
using System.Linq;
using System.Web.Compilation;
using System.CodeDom;
using System.Xml.Linq;
namespace SessionBuildProvider
{
public class TestBuildProvider : BuildProvider
{
public override void GenerateCode(AssemblyBuilder ab)
{
CodeCompileUnit ccu = GenerateClass(@"C:\Users\yzhu.MAGICGRIDS\Documents\Visual Studio 2008\WebSites\SessionTest\App_Code\test.session");
ab.AddCodeCompileUnit(this, ccu);
}
private CodeCompileUnit GenerateClass(string filePath)
{
var doc = XDocument.Load(filePath);
var q = from session in doc.Elements("sessions").Elements("session") select session;
CodeCompileUnit ccu = new CodeCompileUnit();
CodeNamespace cn = new CodeNamespace("Util");
ccu.Namespaces.Add(cn);
CodeTypeDeclaration entityClass = new CodeTypeDeclaration("MyObj");
cn.Types.Add(entityClass);
CodeTypeDeclaration sessionClass = new CodeTypeDeclaration("Sessions");
CodeTypeConstructor defaultConstructor = new CodeTypeConstructor();
defaultConstructor.Statements.Add(new CodeSnippetStatement("obj = new MyObj(); System.Web.HttpContext.Current.Session[\"data\"] = obj;"));
sessionClass.Members.Add(defaultConstructor);
CodeMemberField objField = new CodeMemberField(new CodeTypeReference("MyObj"), "obj");
objField.Attributes = MemberAttributes.Static;
sessionClass.Members.Add(objField);
cn.Types.Add(sessionClass);
foreach (var s in q)
{
CodeMemberField field = new CodeMemberField(new CodeTypeReference(s.Attribute("Type").Value), s.Attribute("Name").Value.ToLower());
entityClass.Members.Add(field);
CodeMemberProperty prop = new CodeMemberProperty();
prop.Name = s.Attribute("Name").Value;
prop.Type = new CodeTypeReference(s.Attribute("Type").Value);
prop.Attributes = MemberAttributes.Public;
prop.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), s.Attribute("Name").Value.ToLower())));
prop.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), s.Attribute("Name").Value.ToLower()), new CodePropertySetValueReferenceExpression()));
entityClass.Members.Add(prop);
prop = new CodeMemberProperty();
prop.Name = s.Attribute("Name").Value;
prop.Type = new CodeTypeReference(s.Attribute("Type").Value);
prop.Attributes = MemberAttributes.Public | MemberAttributes.Static;
prop.GetStatements.Add(new CodeSnippetExpression(string.Format("return ((MyObj)System.Web.HttpContext.Current.Session[\"data\"]).{0};", s.Attribute("Name").Value)));
prop.SetStatements.Add(new CodeAssignStatement(new CodeSnippetExpression(string.Format("((MyObj)System.Web.HttpContext.Current.Session[\"data\"]).{0}", s.Attribute("Name").Value)), new CodePropertySetValueReferenceExpression()));
sessionClass.Members.Add(prop);
}
return ccu;
}
}
}
然後在web.config進行配置,放到compilation節點下:<buildProviders>
<add extension=".session" type="SessionBuildProvider.TestBuildProvider"/>
</buildProviders>
然後在app_code目錄中加一個.session設定檔test.session(由於是demo代碼,上面我直接寫入程式碼了路徑,可以從BuildProvider基類的VirtualPath屬性擷取路徑):<?xml version="1.0" encoding="utf-8"?>
<sessions>
<session Type="System.Int32" Name="UserID" Key="userid" />
<session Type="System.String" Name="UserName" Key="username" />
</sessions>
使用一個設定檔來定義會話的索引值等也可以方便團隊協作,避免會話使用上的衝突等。
最後就可以在寫代碼的時候直接這麼使用了: Sessions.UserID = 1;
Response.Write(Sessions.UserID);
Sessions.UserName = "hello";
Response.Write(Sessions.UserName);
資料並沒有直接儲存在Session中,而是全部儲存在MyObject大對象中,大對象再完整儲存到了Session中。
ViewState、Session、Cache的封裝其實都可以這麼實現,完全可以複雜一點,把值範圍檢測或對象的生命週期管理也自動產生進去。
由於是demo代碼,其中還有很多不完善的地方,大家可以順這個思路自己去實現。
對於QueryString的封裝也可以這樣,當然缺點就是不夠靈活,而且由於從QueryString中擷取的資料也就是簡單實值型別和string,應該可以這樣: public static Nullable<T> GetQueryString<T>(this Page p, string param)
where T : struct
{
string s = p.Request.QueryString[param];
Nullable<T> r = null;
if (s == null)
return r;
try
{
r = (T)Convert.ChangeType(s, typeof(T));
}
catch
{
}
return r;
}
public static string GetQueryString(this Page p, string param)
{
return p.Request.QueryString[param];
}
使用的時候: Debug.Assert(this.GetQueryString<int>("i") == null, "i == null");
Debug.Assert(this.GetQueryString("s") == null, "s == null");
不知大家還有沒有更好的方法?