有关一些状态机制的封装
直接使用字符串访问会话字典的方式有几个缺点:
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");
不知大家还有没有更好的方法?
欢迎大家阅读我的极客时间专栏《Java业务开发常见错误100例》【全面避坑+最佳实践=健壮代码】

分类:
webform
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构