最近在学习ASP.NET 2.0,昨天看到了“应用程序数据缓存”中的“缓存依赖”章节,根据书上例子需要实现的功能,自己先按自己的思路编写了一个程序。在调试的过程中发现了一个有关缓存清除的问题。以下是我实现的过程:
- 通过vs.net 2005建立一个网站项目,然后在其中建立一个新的页面,取名为“CacheDependency.aspx”;
- 在App_Data文件夹中建立一个XML文件,用于页面显示的数据源,取名为Computer.xml。XML文件内容如下:
< item choice ="Intel" price ="1000" url ="http://www.ibm.com" />
< item choice ="AMD" price ="850" url ="http://www.amd.com.cn" />
< item choice ="Microsoft" price ="1000" url ="http://www.microsoft.com.cn/msdn/" />
</ hardware >
很容易理解,这里显示的是有关计算机的信息,每条记录中包含3个属性,分别为提供商名称、价格、网站。
3. 在CacheDependency.aspx页面中添加如下控件(均为服务器控件):
序号 |
控件类型 |
控件ID |
说明信息 |
1 |
GridView |
GridView1 |
用于显示数据源的信息。 |
2 |
TextBox |
TextBox1 |
用于填写新的提供商名称。 |
3 |
TextBox |
TextBox2 |
用于填写新的价格。 |
4 |
TextBox |
TextBox3 |
用于填写新的网址(URL)。 |
5 |
Button |
Button1 |
用于触发更新XML数据源的过程。 |
6 |
Label |
Label1 |
用于在缓存(Cache)清除时,显示提示信息和清除原因。 |
界面设计如图:
4. 在页面后台代码中添加如下代码,如下图所示:
Imports System.Data
Imports System.Xml
Partial Class CacheDependency
Inherits System.Web.UI.Page
Private Const strFilePath As String = "C:Documents and Settings ujunMy DocumentsVisual Studio 2005ASPDotNET2App_DataComputer.xml"
Private removedReason As String
'从XML文件中读取数据,并将包含数据的DataTable进行返回;
Public Function LoadXMLFile() As DataTable
If (File.Exists(strFilePath) = True) Then
Dim ds As New DataSet
ds.ReadXml(strFilePath)
Return (ds.Tables(0))
Else
Me.MsgBox("作为数据源的XML文件不存在!请检查。")
Return (Nothing)
End If
End Function
'根据DataTable中的内容生成缓存;
Public Sub UpdateCache(ByVal dtComputerInfo As DataTable)
Dim dep As New System.Web.Caching.CacheDependency(strFilePath, DateTime.Now)
Cache.Insert("computerInfo", dtComputerInfo, dep, DateTime.MaxValue, TimeSpan.Zero, CacheItemPriority.Default, New CacheItemRemovedCallback(AddressOf CacheItemRemoved))
End Sub
'根据文本框的输入更新XML文件;
Public Sub ModifyXMLFile()
If (File.Exists(strFilePath) = True) Then
Dim dtXMLFile As DataTable = Me.LoadXMLFile()
If (Not dtXMLFile Is Nothing) Then
Dim dr As DataRow = dtXMLFile.NewRow
'如果TextBox中为空,那么替换空字符串(String.Empty)的默认值;
Dim defaultValue As String = "--"
If (TextBox1.Text <> String.Empty) Then
dr.Item("choice") = TextBox1.Text
Else
dr.Item("choice") = defaultValue
End If
If (TextBox2.Text <> String.Empty) Then
dr.Item("price") = TextBox2.Text
Else
dr.Item("price") = defaultValue
End If
If (TextBox3.Text <> String.Empty) Then
dr.Item("url") = TextBox3.Text
Else
dr.Item("url") = defaultValue
End If
dtXMLFile.Rows.Add(dr)
'通过文件流,重新向XML文件中写入新的数据源内容;
Dim fs As New FileStream(strFilePath, FileMode.Open, FileAccess.Write, FileShare.ReadWrite)
dtXMLFile.WriteXml(fs)
fs.Close()
'重新绑定GridView
Me.BoundGridView(dtXMLFile)
'Cache.Remove("computerInfo")
'由于缓存(Cache)以来的XML文件的内容发生了变化,Cache将被清除,
'这里需要通过调用此函数来重建缓存和缓存依赖项;
Me.UpdateCache(dtXMLFile)
Else
Me.MsgBox("dtXMLFile为空(Nothing)")
End If
Else
Me.MsgBox("作为数据源的XML文件不存在!请检查。")
Return
End If
End Sub
'用于弹出提示窗口;
Public Sub MsgBox(ByVal message As String)
ClientScript.RegisterStartupScript(ClientScript.GetType(), "reminderInfo", message)
End Sub
'缓存(Cache)清除时的回调函数(CallBack),在页面中显示缓存清除的原因;
Private Sub CacheItemRemoved(ByVal key As String, ByVal value As Object, ByVal removeReason As CacheItemRemovedReason)
Label1.Text = "移除Cache项的原因是: " & removeReason.ToString()
End Sub
'将GridView控件绑定到数据源以显示数据源信息;
Private Sub BoundGridView(ByVal dtDataSource As DataTable)
If (Not dtDataSource Is Nothing) Then
GridView1.DataSource = dtDataSource
GridView1.DataBind()
End If
End Sub
'页面载入函数;判断是否存在缓存,如果存在缓存就直接通过将GridView绑定到缓存数据来显示信息;
'如果不存在缓存则通过读取XML文件内容,重新建立缓存;
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If (Not Page.IsPostBack) Then
Dim dtDataSource As DataTable
If (Cache("computerInfo") Is Nothing) Then
dtDataSource = Me.LoadXMLFile()
Me.UpdateCache(dtDataSource)
Else
dtDataSource = CType(Cache("computerInfo"), DataTable)
End If
Me.BoundGridView(dtDataSource)
End If
End Sub
'触发XML修改的按钮响应函数;
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
Me.ModifyXMLFile()
End Sub
End Class
这里需要注意的就是上面代码被注释掉的"Cache.Remove("computerInfo")"这一句。当不使用这句代码的时候,如果你使用代码,你会发现Label1中显示Cache被清除的信息是随即的,也就是可能你通过修改XML文件改变缓存依赖项从而触发了Cache失效清除,但是清除可能不是立刻执行的。如果对代码进行调试,那么你会发现作为Cache清除时的回调函数"CacheItemRemoved"也不是每次执行,而且即便是执行也是随即的,不是一定在执行到某一句后就会触发。这里就说明一个问题Cache的清除就和GC的过程一样是不可控制的。至于是不是就是使用GC来回收,感觉上很有可能,但是还没有确认。后来再去看书的时候,发现作者其实为什么在这里显式的调用Cache.Remove方法也做出了解释,“Cache.Remove方法在此似乎多余,但是由于本实例需要显示缓存对象移除的原因,因此,需要实现CacheItemRemoved方法。该方法由CacheItemRemovedCallback委托定义,其调用的时候必须等到调用Remove方法时,所以这里采用显式调用的方法,虽然从缓存清除的功能上确实多余,但是为了保证每次都在Label1中显示Cache清除的信息这句代码还是必要的。”
P.S. 到了这里说点别的,如果有编程的基础,那么最好是先自己实现例子需要实现的功能,然后再和提供的例子程序进行对比。这样往往可以发现一些仅仅就是看看代码,或者直接运行一下例子程序不能发现的细节问题。^_^