直接使用字符串访问会话字典的方式有几个缺点:
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;
}
}
}
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 >
< 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 >
< 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);
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];
}
使用的时候:
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 " );
Debug.Assert( this .GetQueryString( " s " ) == null , " s == null " );
不知大家还有没有更好的方法?