当前位置: 移动技术网 > IT编程>开发语言>.net > 在ASP.NET 2.0中操作数据之五十九:使用SQL缓存依赖项SqlCacheDependency

在ASP.NET 2.0中操作数据之五十九:使用SQL缓存依赖项SqlCacheDependency

2017年12月12日  | 移动技术网IT编程  | 我要评论

飞秀连连看,天行加速器官网,河南人事局

导言:

  在56和57章探讨的缓存技术使用的是基于时间的缓存周期,当过了某段时间后便将缓存数据从内存清除。当设置缓存时间为x秒时,数据在x秒内都是“新”的。当然,就像在60章谈到的那样,对静态数据来说,x可延伸到web应用程序的整个生命周期(lifetime)。

  当缓存数据时,基于时间周期的技术因为其易用性而常常被采用,不过又常常不那么完美。理想的状态是这样的:数据库数据还是应缓存在内存,直到源数据(underlying data)发生改变时才从内存清除。这样的话可以最大化的获取缓存带来的性能上的好处,同时使“过时数据”(stale data)持续的时间最短。然而,为此,我们需要建立一种机制来探测数据库数据什么时候发生了改变,并将对应的缓存条目清除掉。

  asp.net 2.0提供的sqlcachedependency class类和必要的下部基础构造(infrastructure)可以判断数据库什么时候发生了更改,以便将对应的缓存条目从内存清除掉。有2种技术可以判断源数据在什么时候发生了改变:polling 和notification。讨论完这2者之间的差别后,我们将创建必要的下部基础构造来支持polling,然后探讨如何使用sqlcachedependency class类

理解notification and polling

  正如前面所述,有2种方法来判断一个数据库里的数据在什么时候修改过:notification 和 polling.当使用notification的时候,数据库提示(alerts)asp.net对应某个具体查询的数据已经发生了改变;于是对应的缓存条目将被清除。使用polling的时候,数据库服务器将包含某个表(tables)最近发生更改时的相关信息。asp.net周期性的对数据库进行检查,看哪些表在数据被缓存以后发生过改动,若改动过,对应的缓存条目将被清除。

  notification是对查询(query)而不是表(table)进行跟踪检查,相对polling而言,需要采取的步骤要少些。不过遗憾的是,只有在microsoft sql server 2005的完整版(也就是non-express版本)才能使用该功能。而microsoft sql server的所有版本,从7.0 到2005都可以使用polling功能,因为本系列教程使用的是sql server 2005的express版本,在此我们将集中探讨建立和使用polling。关于sql server 2005的notification功能,你可以参阅本文结束部分的further reading。

  要使用polling,我们将设置数据库包含一个名为aspnet_sqlcachetablesforchangenotification的表。该表有3列:tablename, notificationcreated, 和changeid.对于哪些在web应用程序的sql cache dependency里要用到的表,该表都有一条记录与之对应。tablename就是具体某个表的名称;notificationcreated指明了添加记录时的date 和 time;而列changeid的类型是int,初始值是0,每当对应的表发生一次改动,其值就自动增加一次。

  除了表aspnet_sqlcachetablesforchangenotification外,数据库还需要为出现在sql cache dependency里的每个表包含一个触发器(triggers),任何时候,只要表插入、更新、删除一条记录或在表aspnet_sqlcachetablesforchangenotification里的对应的changeid值增大的情况下就会执行触发器。

  当使用sqlcachedependency对象(object)来缓存数据时,asp.net将关注某个表的当前(current)的changeid值,一旦发现当前其值与数据库里面的changeid值不同时,就将该sqlcachedependency对象清除。因为,changeid不吻合就意味着在完成数据缓存后,表又发生过改动。

第一步:考察命令行程序aspnet_regsql.exe

  如上所述,使用polling方法时,必须对数据库进行设置以包含这些基础构造:一个预先定义的表(aspnet_sqlcachetablesforchangenotification),一些存储过程,以及基于在sql cache dependencies里要用到的表的触发器。诸如这些表、存储过程、触发器等都可以通过命令行程序aspnet_regsql.exe来创建,该命令位于$windows$/microsoft.net/framework/version文件夹。要创建表aspnet_sqlcachetablesforchangenotification以及相关的存储过程,可以在命令行这样运行:

复制代码 代码如下:
/* for sql server authentication... */
aspnet_regsql.exe -s server -u user -p password -d database -ed

复制代码 代码如下:
/* for windows authentication... */
aspnet_regsql.exe -s server -e -d database -ed

  注意:要运行这些命令,必须以db_securityadmin 和 db_ddladmin的身份登录数据库,更多详情请参阅作者博客:

比如:在windows身份认证模式下,对某个数据库服务器scottsserver里的数据库pubs添加基础构造时,在命令行键入:

复制代码 代码如下:
aspnet_regsql.exe -s scottsserver -e -d pubs -ed

完成了数据库级(database-level)基础构造的添加后,我们需要添加触发器,再次使用aspnet_regsql.exe命令,不过用-t来指定"表名"(table name),且将-ed替换为-et,如下:

复制代码 代码如下:
/* for sql server authentication... */
aspnet_regsql.exe -s <i>server</i>
-u <i>user</i> -p <i>password</i> -d <i>database</i> -t <i>tablename</i> -et

复制代码 代码如下:
/* for windows authentication... */
aspnet_regsql.exe -s <i>server</i>
-e -d <i>database</i> -t <i>tablename</i> -et

要对scottsserver里的表authors和titles添加触发器,这样做:

复制代码 代码如下:
aspnet_regsql.exe -s scottsserver -e -d pubs -t authors -et
aspnet_regsql.exe -s scottsserver -e -d pubs -t titles -et

就本文而言,我们要对表products, categories,和suppliers添加触发器,具体的命令在第三步探讨。

第二步:在文件夹app_data里引用一个microsoft sql server 2005 express版的数据库

  我们刚刚说过,为了添加必要的基础构造,aspnet_regsql.exe命令需要用到数据库和服务器的名称。但是对于放在文件夹app_data里的一个microsoft sql server 2005 express的数据库而言,它的数据库名和服务器名又是什么呢?犯不着探究其数据库名和服务器名到底是什么,我发现最简单的方法是用sql服务管理器(sql server management studio)来将该数据库认作localhost/sqlexpress database数据库,并重新命名。如果你的机器里已经安装了sql server 2005完整版,自然也就安装了sql服务管理器。如果你安装的是express版本的话,你可以免费下载microsoft sql server management studio express edition.
()

首先,关闭visual studio,然后打开sql server management studio,在windows authentication模式里选择连接到localhost/sqlexpress.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908232365.jpg
图1:连接到localhost/sqlexpress server

  连接到服务器后,管理器将显示服务器,并将数据库、安全等以折叠的形式显示出来。在数据库文件夹上右击,选添加(attach)项,这样将弹出attach databases对话框(见图2),点add按钮,选择我们的web应用程序的app_data文件夹里的northwnd.mdf数据库。

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908233650.jpg
图2:选app_data文件夹里的northwnd.mdf数据库

  这样将会把数据库添加到databases文件夹,且数据库的名称可能是该数据库文件的绝对路径(full path).出于简便的原则,我们将其重命名为一个更友好(human-friendly)的名字,我将其命名为“datatutorials”.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908234936.jpg
图3:将新添加的数据库重命名

第三步:对northwind数据库添加polling基础构造

现在我们添加了app_data文件夹里的northwnd.mdf数据库,让我们添加polling 基础构造吧,假定你已经将数据库重命名为“datatutorials”, 运行如下的命令:

复制代码 代码如下:
aspnet_regsql.exe -s localhost/sqlexpress -e -d datatutorials -ed
aspnet_regsql.exe -s localhost/sqlexpress -e -d datatutorials -t products -et
aspnet_regsql.exe -s localhost/sqlexpress -e -d datatutorials -t categories -et
aspnet_regsql.exe -s localhost/sqlexpress -e -d datatutorials -t suppliers -et

完成上述4个命令后,在management studio里右击数据库,进入任务子菜单(tasks submenu),选分派(detach)。然后关闭management studio并重新打开visual studio.

打开visual studio后,在服务器资源管理器里展开数据库,你可以看到有新增的表(aspnet_sqlcachetablesforchangenotification),新的存储过程,以及对应于表products, categories, 和suppliers的触发器.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908236221.jpg
图4:数据库包含了必需的polling基础构造

第四步:设置polling服务

完成上述步骤后,最后我们需要设置polling服务。这要用到web.config文件,在里面指定要用到的数据库,以及检测频率(polling frequency),单位为毫秒。下面的代码是每隔1秒对northwind数据库检测一次。

<?xml version="1.0"?>
<configuration>
 <connectionstrings>
 <add name="northwndconnectionstring" connectionstring=
  "data source=./sqlexpress;attachdbfilename=|datadirectory|/northwnd.mdf;
  integrated security=true;user instance=true"
  providername="system.data.sqlclient"/>
 </connectionstrings>
 <system.web>
 ...

 <!-- configure the polling service used for sql cache dependencies -->
 <caching>
  <sqlcachedependency enabled="true" polltime="1000" >
  <databases>
  <add name="northwinddb"
   connectionstringname="northwndconnectionstring" />
  </databases>
  </sqlcachedependency>
 </caching>
 </system.web>
</configuration>

  在<add>元素里的name值(“northwinddb”)是一个易读(human-readable)的名称,它与某个具体的数据库对应。当使用sql cache dependencies的时候,我们需要引用在这里定义的数据库名。我们将在第六步考察怎样使用sqlcachedependency class类来缓存数据。

  一旦确定了一个sql cache dependency后,检测系统(polling system)每隔定义的polltime那么多毫秒对<databases>元素里的数据库进行连接,并执行名为aspnet_sqlcachepollingstoredprocedure的存储过——该存储过程是我们在第三步使用aspnet_regsql.exe命令行工具添加的,它返回的是表aspnet_sqlcachetablesforchangenotification里的每条记录的tablename 和 changeid值。那些“过时”的sql cache dependencies将会从内存清除掉。

  应在权衡性能和数据刷新(data staleness)的基础上设置polltime.较小的polltime值虽然导致请求数据库的次数增加,但能更快的将“过时"的数据清除掉;较大的polltime值虽然减少了对数据库的请求次数,但增加了“过时”的缓存条目的呆滞时间。还好,对数据库的请求只是执行一个简单的存储过程而已,该存储过程仅仅从一个简单的表返回很少的几行。你最好多测试几个不同的polltime值,在平衡数据库访问和数据刷新2方面的情况下找出一个理想的值。polltime值最小允许为500.

  注意:在上面的代码里,我们在<sqlcachedependency>元素里指定了一个单一的polltime值。其实你也可以在<add>元素里随意的指定一个polltime值。当你指定了很多个数据库,且你想为每个数据库都指定一个检测频率(polling frequency)时,这样做很有用。

第五:声明sql cache dependencies

  在第一到第四步骤,我们探讨了如何建立必需的数据库基础构造,以及设置检测系统(polling system).完成上述步骤后,现在我们可以通过编程或声明的方式,在添加缓存条目时使用sql cache dependency.在本节,我们探讨如何使用声明的方式使用sql cache dependencies,在第六步再探讨通过编程的方式。

  在《使用objectdatasource缓存数据》教程里,我们考察了声明objectdatasource控件的缓存功能。仅仅将enablecaching属性设置为true,并将acheduration属性设置为某个时间间(timeinterval),objectdatasource控件就会自动地将从“源对象”(underlying object)返回的数据进行缓存。objectdatasource控件可以使用单个或多个sql cache dependencies.

  为此,打开文件夹caching里的sqlcachedependencies.aspx页面,在设计模式里,从工具箱拖一个gridview控件到页面上,设置其id为productsdeclarative ,从其智能标签里将其绑定到一个名为productsdatasourcedeclarative的objectdatasource.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908234278.jpg
图5:创建一个名为productsdatasourcedeclarative的objectdatasource

  设置该objectdatasource使用productsbll类。在select标签里选getproducts()方法;在update标签里,选择包含3个输入参数——productname,unitprice,和productid的updateproduct重载方法;在insert 和 delete标签里选“(none)”.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908245279.jpg
图6:使用包含3个输入参数的updateproduct重载方法

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908243336.jpg
图7:在insert和delete标签的下拉列表里选“(none)”

  完成设置后,visual studio会为gridview里的每一列创建绑定列(boundfields) 和checkboxfieldsl列。将productname, categoryname, 和unitprice以外的列都删除,对其应用什么格式化都可以。在gridview的智能标签里启用分页、排序、编辑功能。visual studio会将objectdatasource控件的oldvaluesparameterformatstring属性设置为original_{0},为使gridview的编辑功能运行正常,要么删除该属性,要么将其设置为默认值:{0}.

  最后,在gridview上面添加一个label web控件,设置其id为odsevents,再将其enableviewstate属性设置为false.做完上述修改后,页面的声明代码看起来应该和下面的差不多。注意,我已经对gridview列的外观做了些定制,虽然这对sql cache dependency功能来说并不是必要的。

<asp:label id="odsevents" runat="server" enableviewstate="false" />

<asp:gridview id="productsdeclarative" runat="server"
 autogeneratecolumns="false" datakeynames="productid"
 datasourceid="productsdatasourcedeclarative"
 allowpaging="true" allowsorting="true">
 <columns>
 <asp:commandfield showeditbutton="true" />
 <asp:templatefield headertext="product" sortexpression="productname">
  <edititemtemplate>
  <asp:textbox id="productname" runat="server"
   text='<%# bind("productname") %>' />
  <asp:requiredfieldvalidator id="requiredfieldvalidator1"
   controltovalidate="productname" display="dynamic"
   errormessage="you must provide a name for the product."
   setfocusonerror="true"
   runat="server">*</asp:requiredfieldvalidator>
  </edititemtemplate>
  <itemtemplate>
  <asp:label id="label2" runat="server"
   text='<%# bind("productname") %>' />
  </itemtemplate>
 </asp:templatefield>
 <asp:boundfield datafield="categoryname" headertext="category"
  readonly="true" sortexpression="categoryname" />
 <asp:templatefield headertext="price" sortexpression="unitprice">
  <edititemtemplate>
  $<asp:textbox id="unitprice" runat="server" columns="8"
   text='<%# bind("unitprice", "{0:n2}") %>'></asp:textbox>
  <asp:comparevalidator id="comparevalidator1" runat="server"
   controltovalidate="unitprice"
   errormessage="you must enter a valid currency value with
   no currency symbols. also, the value must be greater than
   or equal to zero."
   operator="greaterthanequal" setfocusonerror="true"
   type="currency" display="dynamic"
   valuetocompare="0">*</asp:comparevalidator>
  </edititemtemplate>
  <itemstyle horizontalalign="right" />
  <itemtemplate>
  <asp:label id="label1" runat="server"
   text='<%# bind("unitprice", "{0:c}") %>' />
  </itemtemplate>
 </asp:templatefield>
 </columns>
</asp:gridview>

<asp:objectdatasource id="productsdatasourcedeclarative" runat="server"
 selectmethod="getproducts" typename="productsbll"
 updatemethod="updateproduct">
 <updateparameters>
 <asp:parameter name="productname" type="string" />
 <asp:parameter name="unitprice" type="decimal" />
 <asp:parameter name="productid" type="int32" />
 </updateparameters>
</asp:objectdatasource>

下一步,为objectdatasource控件的selecting事件创建一个事件处理器:

protected void productsdatasourcedeclarative_selecting
 (object sender, objectdatasourceselectingeventargs e)
{
 odsevents.text = "-- selecting event fired";
}

  我们知道,只有当objectdatasource控件从它的相关“源对象”(underlying object)获取数据时才会触发它的selecting事件。如果objectdatasource是从内存检索数据的话,将不会触发selecting事件.

  现在,在浏览器里登录该页面,因为我们还没有进行缓存,所以每当你分页、排序、编辑时,页面都会显示文本——“—selecting event fired”, 如图8所示:

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908242865.jpg
图8:当分页、排序、编辑时都会触发objectdatasource的selecting事件。

  就像我们在教程《使用objectdatasource缓存数据》里探讨的一样,除了enablecaching属性以外,objectdatasource控件还有sqlcachedependency property属性,它可以为缓存数据添加一个或更多的sql cache dependencies.像下面这样:

databasename1:tablename1;databasename2:tablename2;...

  其中,databasename是web.config文件里<add>元素的name属性指定的数据库名,而tablename就是数据库里的一个表。举个例,要创建一个objectdatasource,它用sql cache dependency来缓存数据,当我们指定要用northwind数据库里的 products表时,我们将objectdatasource的enablecaching属性设置为true,且sqlcachedependency属性为“northwinddb:products”.
注意:你可以通过设置enablecaching属性为true来使用一个 sql cache dependency和基于时间的缓存期(time-based expiry)。cacheduration对应时间间隔;sqlcachedependency对应数据库名和表名。不管是缓存到期还是检查系统(polling system)发现“源数据”发生改变,只要其中一个发生, objectdatasource 都会将清除其数据。

  在sqlcachedependencies.aspx页面里的gridview 控件从2个表获取数据——products 和 categories (产品的categoryname列是通过语法 join on categories来获取的). 因此,我们想指定2个sql cache dependencies:
“northwinddb:products;northwinddb:categories”.

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908248164.jpg
图9:设置objectdatasource支持缓存,且使用基于表products 和 categories的sql cache dependencies

  设置sql cache dependencies支持缓存后,再次来浏览器里登录页面。最开始,文本“—selecting event fired”依然会出现在页面里,但当进行分页、排序或点击编辑和取消按钮时,文本就消失了。这是因为对数据进行缓存后,其缓存状态一直持续,直到products 或 categories表发生了改变,或我们通过gridview对数据进行了更新。

  做个实验,在第一个浏览器窗口进行分页操作,请注意文本“—selecting event fired”并没有显现出来。再打开第2个浏览器窗口,导航到页面basics.aspx页面(~/editinsertdelete/basics.aspx). 对某个产品的name 或 price进行更新。 再次返回到第一个浏览器窗口,查看下一个页面或进行排序操作或点击某行的编辑按钮,这一次,文本“—selecting event fired又出现了,这是因此“源数据”发生了更改(见图10)。如果文本没有出现,请稍等一下再试一回。我们知道,polling服务每隔设定的polltime那么多毫秒对products表进行检查,开是否改动过。因此在源数据的更新和“过时”数据的清除之间有个延迟期。

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908242707.jpg
图10:改动products表将导致清除“过时”的product缓存数据

第六步:通过编程的方式处理sqlcachedependency类

  在教程《》我们看到了使用单独的缓存层 caching layer的好处。在那篇教程,我们创建了一个productscl 类来处理data cache.要在缓存层caching layer利用sql cache dependencies的话,要用到sqlcachedependency 类。

  在检测系统(polling system)里,一个sqlcachedependency对象必须与某个具体的数据库和表挂钩。下面的代码,创建了一个sqlcachedependency对象,它基于northwind数据库的products表:

caching.sqlcachedependency productstabledependency =
 new caching.sqlcachedependency("northwinddb", "products");

  上面的2个参数分别对应数据库名和表名。与objectdatasource控件的属性sqlcachedependency类似,数据库名是使用的web.config.文件里<add> 元素的name属性指定的值,而表名是实际的数据库表名.。

  要将一个sqlcachedependency与添加到内存的条目联系起来,可以使用一个重载的接受dependency的insert方法。下面代码里的sqlcachedependency基于表products,且缓存时间未定。换句话说,数据会一直保存在内存,除非内存不足或表products发生了改变才被清除掉。

caching.sqlcachedependency productstabledependency =
 new caching.sqlcachedependency("northwinddb", "products");
cache.insert(key,
  value,
  productstabledependency,
  system.web.caching.cache.noabsoluteexpiration,
  system.web.caching.cache.noslidingexpiration);

  目前,缓存层caching layer的productscl类从表products获取数据,缓存时间为60秒。 让我们对其进行更新,使其使用sql cache dependencies. 类productscl的addcacheitem方法是用来向内存添加数据的,其当前代码如下:

private void addcacheitem(string rawkey, object value)
{
 system.web.caching.cache datacache = httpruntime.cache;

 // make sure mastercachekeyarray[0] is in the cache
 datacache[mastercachekeyarray[0]] = datetime.now;

 // add a cachedependency
 caching.cachedependency dependency =
 new caching.cachedependency(null, mastercachekeyarray);
 datacache.insert(getcachekey(rawkey), value, dependency,
 datetime.now.addseconds(cacheduration),
 system.web.caching.cache.noslidingexpiration);
}

  让我们对其进行更新,用一个sqlcachedependency对象来替换掉mastercachekeyarray cache dependency:

  我们来进行测试。在名为productsdeclarative的gridview控件下再添加一个gridview,设置其id为productsprogrammatic,在其智能标里将其绑定到一个名为productsdatasourceprogrammatic的新的objectdatasource,设置该objectdatasource使用productscl类,分别在select 和 update标签里选getproducts 和 updateproduct方法。

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908252236.jpg
图11:设置新objectdatasource使用productscl类

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908254994.jpg
图12:在select标签里选getproducts方法

http://www.lhsxpumps.com/_images/10qianwan/20171212/b_1_201712121908255308.jpg
图13:在update标签选updateproduct方法

  完成设置后,visual studio会自动地为gridview控件添加boundfields和 checkboxfields。就像上面那个gridview控件一样,将productname, categoryname, 和 unitprice以外的列都删除掉。在其智能标签里,启用分页、排序、编辑功能。同时,为使gridview控件的编辑功能正常工作,将oldvaluesparameterformatstring属性改成默认值{0}. 或干脆在代码声明里将该属性删除。

完成上述修改后,最终的gridview 和 objectdatasource的声明代码看起来应该和下面的差不多:

<asp:gridview id="productsprogrammatic" runat="server"
 autogeneratecolumns="false" datakeynames="productid"
 datasourceid="productsdatasourceprogrammatic" allowpaging="true"
 allowsorting="true">
 <columns>
 <asp:commandfield showeditbutton="true" />
 <asp:templatefield headertext="product" sortexpression="productname">
  <edititemtemplate>
  <asp:textbox id="productname" runat="server"
   text='<%# bind("productname") %>' />
  <asp:requiredfieldvalidator id="requiredfieldvalidator1" 
   controltovalidate="productname" display="dynamic"
   errormessage="you must provide a name for the product."
   setfocusonerror="true"
   runat="server">*</asp:requiredfieldvalidator>
  </edititemtemplate>
  <itemtemplate>
  <asp:label id="label2" runat="server"
   text='<%# bind("productname") %>' />
  </itemtemplate>
 </asp:templatefield>
 <asp:boundfield datafield="categoryname" headertext="category"
  readonly="true" sortexpression="categoryname" />
 <asp:templatefield headertext="price" sortexpression="unitprice">
  <edititemtemplate>
  $<asp:textbox id="unitprice" runat="server" columns="8"
   text='<%# bind("unitprice", "{0:n2}") %>'></asp:textbox>
  <asp:comparevalidator id="comparevalidator1" runat="server"
   controltovalidate="unitprice" display="dynamic"
   errormessage="you must enter a valid currency value with
   no currency symbols. also, the value must be greater than
   or equal to zero."
   operator="greaterthanequal" setfocusonerror="true"
   type="currency" valuetocompare="0">*</asp:comparevalidator>
  </edititemtemplate>
  <itemstyle horizontalalign="right" />
  <itemtemplate>
  <asp:label id="label1" runat="server"
   text='<%# bind("unitprice", "{0:c}") %>' />
  </itemtemplate>
 </asp:templatefield>
 </columns>
</asp:gridview>

<asp:objectdatasource id="productsdatasourceprogrammatic" runat="server"
 oldvaluesparameterformatstring="{0}" selectmethod="getproducts"
 typename="productscl" updatemethod="updateproduct">
 <updateparameters>
 <asp:parameter name="productname" type="string" />
 <asp:parameter name="unitprice" type="decimal" />
 <asp:parameter name="productid" type="int32" />
 </updateparameters>
</asp:objectdatasource>

  要测试位于缓存层的sql cache dependency,先在productcl类的addcacheitem方法里设置断点(breakpoint),然后启动调试。当你首次登录sqlcachedependencies.aspx页面时,应该可以发生断点,因为是第一次请求数据,且把数据添加到内存。然后,在gridview里跳转到下一页或对某个列排序,这将导致gridview控件查询所需的数据,数据应该还驻存在内存因为表products没有改动过。如果一直无法在内存找到所需的数据,务必确保内存够大,然后再试一次。

  在gridview里多跳转几页,再另外打开一个浏览器窗口,导航到basics.aspx页面(~/editinsertdelete/basics.aspx). 更新一条记录。再回到第一个浏览器窗口,再跳转页面或实施排序。

  此时,你会遇到下面2种情况之一:要么程序发生断点,提示你数据被清除了,原图是数据库发生了改动;要么程序没有发生断点,这意味着页面sqlcachedependencies.aspx显示的是“过时”的数据。如果没有发生断点,很可能是当数据改变时没有触发polling服务(polling service).我们知道,polling服务每隔设定的polltime那么多毫秒对products表进行检查,看是否改动过。因此在源数据的更新和“过时”数据的清除之间有个延迟期。

  注意:延迟很可能是当我们在sqlcachedependencies.aspx页面里的gridview里编辑产品信息时发生的。在教程《》里,我们添加mastercachekeyarray cache dependency来确保数据从内存清除。但在前面我们修改addcacheitem方法时将其替换掉了,因此productscl类将继续显示“过时”的数据,直到检测系统发现products发生过改动。我们将在第七步看如何重新引入mastercachekeyarray cache dependency.

第七步:对缓存条目附加多个dependencies

  我们知道, mastercachekeyarray cache dependency的用处在于:与 product相关的所有条目中,只要其中任意一条的相关数据发生更改后,所有的条目都会被清除掉。举个例,getproductsbycategoryid(categoryid)方法根据指定的categoryid获取多条产品记录并缓存,只要其中任何一条记录被清除掉的话, mastercachekeyarray cache dependency 会确保剩下的其它记录也会被清除掉。

  没有mastercachekeyarray cache dependency的话,就会存在这种可能性,当某个条目更改过后,剩余的条目仍然驻留在内存而显得“过时”。因此,在使用sql cache dependencies的时候包含mastercachekeyarray cache dependency是很重要的。然而,data cache的insert 方法只允许存在一个dependency 对象。

  此外,当使用sql cache dependencies的时候,我们可能要依赖多个表。比如,productscl类的productsdatatable里还包含了每个产品的种类(category)和供应商名称(supplier names).但是在addcacheitem方法里,只依赖 表products.设想,如果用户更新了种类或供应商,那么缓存的product数据仍然驻留在内存,显然已经"过时"了。因此,我们想时缓存的product数据不仅依赖products表,还要依赖categories 和 suppliers 表.

  不过类aggregatecachedependency提供了这个途径,将一个缓存条目与多个dependencies联系起来。首先,创建一个aggregatecachedependency实例;然后用aggregatecachedependency的 add 方法添加设置好的dependencies.当aggregatecachedependency 实例里的任何一个dependencies发生改动以后,缓存条目就会被清除掉。

  下面的代码是更新过的productscl类的 addcacheitem 方法。该方法不仅创建了mastercachekeyarray cache dependency,还创建了基于表products, categories,和 suppliers的多个sqlcachedependency objects对象。再把它们组合起来构成一个名为aggregatedependencies的aggregatecachedependency object 对象,将该对象传递给insert方法.

private void addcacheitem(string rawkey, object value)
{
 system.web.caching.cache datacache = httpruntime.cache;

 // make sure mastercachekeyarray[0] is in the cache and create a depedency
 datacache[mastercachekeyarray[0]] = datetime.now;
 caching.cachedependency mastercachekeydependency =
 new caching.cachedependency(null, mastercachekeyarray);

 // add the sqlcachedependency objects for products, categories, and suppliers
 caching.sqlcachedependency productstabledependency =
 new caching.sqlcachedependency("northwinddb", "products");
 caching.sqlcachedependency categoriestabledependency =
 new caching.sqlcachedependency("northwinddb", "categories");
 caching.sqlcachedependency supplierstabledependency =
 new caching.sqlcachedependency("northwinddb", "suppliers");

 // create an aggregatecachedependency
 caching.aggregatecachedependency aggregatedependencies =
 new caching.aggregatecachedependency();
 aggregatedependencies.add(mastercachekeydependency, productstabledependency,
 categoriestabledependency, supplierstabledependency);

 datacache.insert(getcachekey(rawkey), value, aggregatedependencies,
 caching.cache.noabsoluteexpiration, caching.cache.noslidingexpiration);
}

  对代码进行测试。现在,更改表products、 categories, 或suppliers的话将清除掉缓存数据。另外,当在gridview控件里编辑某个产品的话将调用productscl 类的 updateproduct方法,该方法清除掉 mastercachekeyarray cache dependency,进而导致连锁反应清除掉缓存的productsdatatable.最后的结果是,当下次请求数据时将重新从数据库检索数据。
注意:也可以通过output caching来使用sql cache dependencies.欲见详情,请参考《using asp.net output caching with sql server.》()

结语:

  当缓存数据库数据时,最理想的状态是数据一直驻留在内存,直到数据库发生了改动。在asp.net 2.0,可以通过编程或声明代码的方式使用sql cache dependencies ,该方法面临的挑战是及时检测数据发生的改动。microsoft sql server 2005 的完整版提供了notification功能,该功能向应用程序通告某个数据查询返回的结果已经改变了。而对sql server 2005的express版,以及更旧的版本而言,只有使用polling检测系统了。不过还好,为polling设置必要的构造是很简单的。

  祝编程快乐!

作者简介

  本系列教程作者 scott mitchell,著有六本asp/asp.net方面的书,是4guysfromrolla.com的创始人,自1998年以来一直应用 微软web技术。大家可以点击查看全部教程《[翻译]scott mitchell 的asp.net 2.0数据教程》,希望对大家的学习asp.net有所帮助。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网