直接使用字符串访问会话字典的方式有几个缺点:
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(AssemblyBuilderab)
{
CodeCompileUnitccu
=
GenerateClass(
@"
C:\Users\yzhu.MAGICGRIDS\Documents\VisualStudio2008\WebSites\SessionTest\App_Code\test.session
"
);
ab.AddCodeCompileUnit(
this
,ccu);
}
private
CodeCompileUnitGenerateClass(
string
filePath)
{
vardoc
=
XDocument.Load(filePath);
varq
=
fromsession
in
doc.Elements(
"
sessions
"
).Elements(
"
session
"
)selectsession;
CodeCompileUnitccu
=
new
CodeCompileUnit();
CodeNamespacecn
=
new
CodeNamespace(
"
Util
"
);
ccu.Namespaces.Add(cn);
CodeTypeDeclarationentityClass
=
new
CodeTypeDeclaration(
"
MyObj
"
);
cn.Types.Add(entityClass);
CodeTypeDeclarationsessionClass
=
new
CodeTypeDeclaration(
"
Sessions
"
);
CodeTypeConstructordefaultConstructor
=
new
CodeTypeConstructor();
defaultConstructor.Statements.Add(
new
CodeSnippetStatement(
"
obj=newMyObj();System.Web.HttpContext.Current.Session[\
"
data\
"
]=obj;
"
));
sessionClass.Members.Add(defaultConstructor);
CodeMemberFieldobjField
=
new
CodeMemberField(
new
CodeTypeReference(
"
MyObj
"
),
"
obj
"
);
objField.Attributes
=
MemberAttributes.Static;
sessionClass.Members.Add(objField);
cn.Types.Add(sessionClass);
foreach
(vars
in
q)
{
CodeMemberFieldfield
=
new
CodeMemberField(
new
CodeTypeReference(s.Attribute(
"
Type
"
).Value),s.Attribute(
"
Name
"
).Value.ToLower());
entityClass.Members.Add(field);
CodeMemberPropertyprop
=
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属性获取路径):
<?
xmlversion="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
Pagep,
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
Pagep,
string
param)
{
return
p.Request.QueryString[param];
}
使用的时候:
Debug.Assert(
this
.GetQueryString
<
int
>
(
"
i
"
)
==
null
,
"
i==null
"
);
Debug.Assert(
this
.GetQueryString(
"
s
"
)
==
null
,
"
s==null
"
);
不知大家还有没有更好的方法?