三、后备存储状态项的“复苏”
被后备存储的状态项已经不再存储于SessionState中,但是并不意味着它已经是所谓的垃圾对象,它们依然可以被再次访问。在这种情况下,我们会通过我们指定的后备存储器将相应的状态值以字节数组的形式从存储介质中提取出来,进行反序列化后再次放到SessionState中,我个人将这种机制成为“后备对象的复苏”。
在对后备对象的复苏机制进行进一步讲解之前,我们需要了解一个前提:框架始终维护着每一个状态项运行时信息,这些信息包括:状态项最后一次被访问的时间、状态项的使用范围、状态项当前的存储位置(SessionState或者BackingStore)、以及相关的后备策略信息等。这个列表放在SessionState中。
右面所示的序列图(点击看大图)反映了当我们的程序获取某个状态项时,状态后备机制采用的处理流程:当接收到一个来自对某个状态项的请求时,根据Key值获取该状态项当前的运行时信息。如果运行时信息反映它还存在于SessionState中(Location=Session),则直接从SessionState中返回,并更新它的运行时信息(最后一次被访问时间)。
如果该状态项已经进行了背后存储(Location=BackingStore),则借助相应的后备存储器从存储介质中对应的值以字节数组的形式提取出来。在完成反系列化后再次保存到SessionState中,并更新相应运行时信息(最后一次访问时间和当前位置:BackingStore-〉Session)。最后返回反序列化后的具体状态对象。
四、状态项后备策略的定义
判断一个存在于SessionState中的状态项是否应该被后备存储取决于以下三个方面,当同时满足条件1和2,或者2和3的状态项会被后备存储。
· 针对该状态项的最近一次访问的事件到当前时间的间隔超过了设定的超时时限;
· 状态项的总的字节数超过了设定的需要进行后备存储的下限;
· 当前的请求的URL是否超出了设定的状态作用的范围。
但是我们的状态后备策略并没有直接应用于单个的状态项,而是应用于一个较大的粒度:状态组——若干相关状态项的组合。状态组的结构和应用在它上面的后备策略通过配置进行定义,下面的XML体现的配置大体上的结构。
<states>
<properties>
<property name="UserName" type="System.String"/>
<property name="Position" type="System.String"/>
</properties>
<group name="Profile" inactiveTimeout="00:10:00" minimunTotalBytes="1024" >
<property name="Age" type="System.Int32"/>
<property name="Address" type="System.String"/>
</group>
<group name="Product" inactiveTimeout="00:10:00" minimunTotalBytes="1024" scope="Page1, Page2,Page3" >
<property name="ProductId" type="System.String"/>
<property name="UnitPrice" type="System.Decimal"/>
</group>
</states>
在上面的XML片段中,我们定义两个全局的状态项(UserName和Position)和两个状态组(Profile和Product)。两个状态组中又包含各自的状态项,以及对应的后备策略。inactiveTimeout、minimumTotlaBytes和scope分别表示超时时限、序列化后的最下值和使用的范围。
五、通过代码生成机制帮助你以强类型的方式操作状态
既然所有的状态和数据类型(即可以是系统预定义类型,也可以是自定义类型)都能通过XML的形式表示出来,那么我们就能通过代码生成机制将它们通过代码的形式反映出来。你可以采用CodeDOM+Cutom Tool的方式[可以参考我的文章《从数据到代码》(上篇、下篇)],或者是直接使用T4模板[可以参考我的文章《创建代码生成器可以很简单:如何通过T4模板生成代码?》(上篇、下篇)]。比如说,你可以生成一个继承自Page的类型,比如PageBase,添加如下一个State的属性。(下面的代码仅仅代码大体的结构,并省略的具体的实现)
{
public ExtendedRootStateNode State { get; }
}
public class ExtendedRootStateNode : RootStateNode
{
public string UserName { get; set; }
public string Position { get; set; }
public ProfileGroupStateNode Profile { get; private set; }
public ProductGroupStateNode Product { get; private set; }
}
public class ProfileGroupStateNode : GroupStateNode
{
public int Age { get; set; }
public Gender Gender { get; set; }
public string Address { get; set; }
}
public class ProductGroupStateNode : GroupStateNode
{
public string ProductId { get; set; }
public string ProductName { get; set; }
}
如果让你的所有Web页面都继承自这个PageBase,你可以通过强类型的方式获取或者设置每个状态项了。