引言:前两章研究了表示层和缓存层中的缓存数据。在第56章中,我们讨论了在表示层中设置ObjectDataSource的相关缓存属性来缓存数据。在第57章中,我们讨论了创建一个单独的缓存层。在这两章中,都采用了“反应式加载”来缓存数据。在这种模式下,每次请求数据时,系统首先检查数据是否存在于内存中。如果不是,它从数据源——(如数据库)获取数据,然后将其存储在内存中。这种模式的优点是易于实现。缺点之一是它应该在“请求”时执行。想象一下,在上一章中,我们通过缓存层显示了产品信息。当第一次登录页面时,或者当由于缓存时间结束等原因从内存中清除缓存数据后再次访问页面时,请求只能从数据库中获取数据,因为数据没有存储在内存中。这将比直接从内存中获取数据花费更长的时间。
主动加载可以使用两种模式来预加载数据。在第一种模式下,主动加载使用一些过程来判断底层数据是否发生了变化,并及时更新缓存的数据——。例如,定期检查源数据;或者当源数据发生变化时,立即通知更新。然而,这种模式的缺点是难以实现。您必须创建、管理和实现特定的方法来检查源数据的更改并更新缓存的数据。
另一种模式,也是本文的内容,是在程序启动时将数据加载到内存中。这种模式对于缓存静态数据特别有用,例如在数据库表中查找记录。注:“反应式加载”和“主动式加载”的区别,请参考第《Caching Architecture Guide for .NET Framework Applications》条第《Managing the Contents of a Cache》节:(http://msdn 2 . Microsoft.com/en-us/library/ms 978503 . aspx)。
第一步:决定在程序启动阶段缓存哪些数据。
前两章讨论的反应式加载模式的例子适合处理这些数据:周期性地改变并生成数据不会花费太长时间。但是,如果缓存的数据从未改变,那么被动加载模式的到期就有点多余了。此外,如果要缓存的数据需要很长时间才能产生,当用户请求发现内存为空时,用户会等待很长时间来检索和返回数据。在这方面,我们可以考虑缓存静态数据和在程序启动阶段需要很长时间才能生成的数据。
虽然,数据库有很多动态的、频繁变化的值;但是有相当多的静态值。例如,数据库表“患者”有一个主语言列,其值可以是英语、西班牙语、法语、俄语、日语等。但是,我们不会将“英语”或“法语”等字符串直接存储在“患者”表中,而是将其存储在“语言”表中进行搜索。图1:无名氏的主要语言是英语,而艾德约翰逊的主要语言是俄语。
图1:表语言是患者表使用的查找表。
在编辑或创建新患者的用户界面中,将包含一个下拉列表框,列出“语言”表中的所有语言项目。如果没有缓存,系统会在你每次登录界面的时候查询Languages表,所以没有必要显式浪费。因为“语言”表不会频繁更改。
我们可以使用前面讨论的反应加载模式来缓存数据语言。但是,被动加载模式使用基于时间的过期,这对于静态数据是不必要的。最好的方法是在程序启动阶段预加载。
在本文中,我们将讨论如何缓存“查找表”(例如,语言表是患者表的查找表)和其他静态信息的数据。
步骤2:检查缓存数据的不同方法。
在ASP.NET应用程序中,我们可以使用多种方法来缓存信息。我们在前面的教程中看到的是数据缓存。事实上,我们还可以通过使用静态成员或应用程序状态来缓存对象。
当处理一个类时,我们应该在访问它的成员之前实例化它。例如,为了调用BLL层中的方法,我们必须首先创建这个类的一个实例:
product bll product API=new product bll();productsAPI。some method();productsAPI。SomeProperty='你好,世界!';在调用SomeMethod或处理SomeProperty之前,我们必须首先用关键字new创建一个类的实例。SomeMethod和SomeProperty应该对应一个特定的实例,这些成员的生命周期取决于对应对象的生命周期。另一方面,静态成员,如变量、属性、方法等。由该类的所有实例共享,因此它们的生命周期与该类一样长。c成员应该由关键字static来标识。
除了静态成员,还可以使用应用程序状态。每个ASP.NET应用程序都包含一个名称/值集,由应用程序的所有页面和用户共享。它可以通过HttpContext类类的Application属性来访问。在页面的背景代码中,我们可以这样访问它:
应用程序['键']=值;对象值=应用程序['键'];数据缓存为缓存数据提供了丰富的API(应用程序接口),基于时间和依赖关系的过期机制,以及缓存项目优先级等。在本文中,我们将看到缓存静态数据的三种技术。
第三步:缓存供应商表的数据。
我们使用的Northwind数据库没有“查找表”,dal层使用的四个表的值都不是静态的。不需要花时间在DAL层添加新的数据库表,然后在BLL层添加新的类和方法。在本教程中,我们假设Suppliers表的数据是静态的,因此我们在程序启动时缓存其数据。
首先,我们在cl文件夹中创建一个名为StaticCache.cs的新类。
图2:在CL文件夹中创建StaticCache.cs类。
我们需要在程序启动时添加一个加载数据的方法。同样,有一种方法可以从内存中返回数据。
[系统。component model . DataObject]公共类StaticCache{私有静态Northwind。suppliers datatable suppliers=null;public static void loadstatic cache(){//Get suppliers-cache使用静态成员变量supplier sbll supplier sbll=new supplier sbll();供应商=供应商全部。GetSuppliers();}[DataObjectMethodAttribute(DataObjectMethodType。选择,真)]公共静态北风。supplier sdatable GetSuppliers(){ return suppliers;}}在上面的代码中,我们在LoadStaticCache()方法中使用了一个静态成员变量suppliers来保存SuppliersBLL类的GetSuppliers()方法返回的结果。LoadStaticCache()方法应该在程序启动阶段调用。一旦数据在启动时加载到内存中,任何需要供应商信息的页面都可以调用StaticCache类的GetSuppliers()方法。因此,访问数据库以获取供应商信息只会发生一次,即在启动阶段。
除了静态成员变量,我们还可以使用应用程序状态或数据缓存。下面的代码修改了使用应用程序状态:的类。
[系统。component model . DataObject]公共类static cache { public static void loadstatic cache(){//Get suppliers-cache使用应用程序状态suppliers ll suppliers ll=new suppliers ll();HttpContext。当前。应用程序['键']=suppliersBLL。GetSuppliers();}[DataObjectMethodAttribute(DataObjectMethodType。选择,真)]公共静态北风。SuppliersDataTable GetSuppliers(){ return HttpContext。当前。应用程序['键']为北风。SuppliersDataTable}}在LoadStaticCache()方法中,供应商信息存储在应用程序变量键中。在GetSuppliers()方法中,它作为Northwind返回。SuppliersDataTable类型。由于我们可以在ASP.NET页面的后台代码中使用Application['key']来访问应用程序状态,因此我们必须在这里使用HttpContext . current . Application[' key ']来获取当前的httpcontext。
同样,我们可以如下使用数据缓存:
[系统。component model . DataObject]公共类static cache { public static void loadstatic cache(){//Get suppliers-cache使用数据缓存suppliers ball=new suppliers ball();HttpRuntime。缓存。插入(/*键*/'键',/*值*/供应商,/*依赖项*/null,/*绝对导出*/缓存。NoAbsoluteExpiration,/* slidingExpiration */Cache。NoSlidingExpiration,/* priority */CacheItemPriority。NotRemovable,/* onRemoveCallback */null);}[DataObjectMethodAttribute(DataObjectMethodType。选择,真)]公共静态北风。supplier sdatable GetSuppliers(){ return HttpRuntime。将['键']缓存为北风。SuppliersDataTable}}向数据缓存中添加条目,但不指定基于时间的过期时间。因此,我们使用系统。web . cache . cache . noabsoluteexpiration和系统。web . cache . cache . no sliding expiration作为输入参数之一。在上面的数据缓存的Insert()方法中,我们指定了缓存条目的优先级,它指示当内存容量不足时应该从内存中删除哪些条目。在这里,我们将优先级设置为不可移除(即null),这确保了当内存不足时不会被移除。
注意:本文下载代码中的StaticCache类使用了静态成员变量技术,关于应用状态和数据缓存技术的代码可以在类文件的注释部分找到。
第四步:程序启动时执行代码。
为了在程序启动时执行代码,我们需要创建一个名为Global.asax的文件。该文件包含应用程序、会话和请求级别事件的事件处理程序。在这个文件中,我们将添加程序启动时要执行的代码。
若要在网站的根目录中添加Global.asax文件,请在Visual Studio解决方案资源管理器中,右键单击网站项目,选择“添加新项”,从“添加新项”对话框中选择“Global”应用程序项目类型,然后单击“添加”按钮。
注意:如果根目录中已经存在Global.asax文件,则“添加新项”对话框中将不会显示“Global”应用程序项目类型。
图3:在根目录中添加Global.asax文件。
默认的Global.asax文件中有五个方法,每个方法都有一个服务器端)脚本标记:
application _ start程序启动时执行。
application _ end程序完成时执行。
application _ error每当程序中出现未处理的异常时发生。
session _ start在创建会话时执行。
session _ end当会话结束或被删除时发生。
Application_Start事件处理程序在程序的生命周期中只发生一次。该程序从第一次请求ASP.NET资源开始,并继续运行,直到程序重新启动。请参考文章《ASP.NET Application Life Cycle Overview》 http://msdn2.microsoft.com/en-us/library/ms178473.aspx了解更多关于程序生命周期的细节。
在本文中,我们只需要向Application_Start方法添加代码,并放心地删除其他方法。在Application_Start中,只需调用StaticCache类的LoadStaticCache()方法。这会加载和缓存供应商信息:
% @ Application Language=' c# ' % script runat=' server ' void Application _ Start(对象发送方,事件参数e) { StaticCache。LoadStaticCache();}/仅此而已}/脚本就行了!在程序的开始,LoadStaticCache()方法将从BLL获取供应商信息,然后将其存储到静态成员变量(或者您在StaticCache类中使用的一些其他缓存存储)中。要进行验证,请在Application_Start方法中设置断点并执行程序。此外,当发出并发请求时,将不会执行Application_Start方法。
图4:用断点验证Application_Start事件处理程序的执行。
注意:如果第一次调试时没有遇到Application_Start断点,那是因为你的程序已经启动了。您可以修改Global.asax或Web.config文件来强制程序重新启动。您只需在这些文件的末尾添加(或删除)一个空行,即可快速重启程序。
第五步:显示缓存的数据。
目前,StaticCache类在程序启动时缓存与供应商相关的数据。为了在表示层中使用这些数据,我们可以通过ObjectDataSource控件或在ASP.NET页面的后台代码中编程来调用StaticCache类类的GetSuppliers()方法。让我们看看如何使用ObjectDataSource和GridView控件来显示缓存的供应商信息。
首先,在文件夹中打开AtApplicationStartup.aspx页面,在“设计”模式下将一个GrIDView控件从工具箱拖到页面上,并将其id设置为Suppliers。然后,从其智能标记中选择并创建一个新的ObjectDataSource,名为SuppliersCachedDataSource,并将其设置为使用StaticCache类的GetSuppliers()方法。
图5:将ObjectDataSource控件设置为使用StaticCache类。
图6:使用GetSuppliers()方法获取缓存的供应商数据。
设置后,Visual Studio将自动为SuppliersDataTable中的每一列添加一个边界字段。因此,GridView和ObjectDataSource控件的声明标记应该如下所示:
asp: GridView ID=' Suppliers ' runat=' server ' AutoGenerateColumns=' False ' DataKeyNames=' suppliierid ' data sourceid=' suppliiescached data source ' EnableVie wState=' False '列asp3360 boundfield data field=' suppliierid ' header text=' suppliierid ' InsertVisible=' False ' ReadOnly=' True ' sort expression=' Supplier ID '/asp: boundfield data field=' company name ' header text='它们都使用bll层的suppliers BLLclass类来获取数据,但不同的是,我们使用StaticCache类在程序开始时缓存数据并返回。可以在StaticCache类的GetSuppliers()方法中设置断点进行验证。
图7:在GridView控件中显示缓存的供应商数据。
结论:
几乎每个数据模型都包含静态数据,通常使用相应的“查找表”。因为这些信息是静态的,所以没有必要每次显示数据时都访问数据库。此外,由于其“静态”性质,在缓存数据时没有必要设置过期时间。在本文中,我们已经看到了如何使用数据缓存、应用程序状态和静态成员变量来缓存数据。这些数据在程序启动时被缓存,并将在程序的整个生命周期中保留在内存中。
在本文和前两章中,我们讨论了在程序生命周期中缓存数据和使用基于时间的过期。缓存数据库数据时,如果底层数据库数据发生变化,我们应该删除相应的缓存条目。在处理这个问题时,虽然使用基于时间的缓存周期的方法并不完美,但与通过编程“刷新”数据相比,它仍然是一个很好的解决方案。也许最好的解决方案是使用SQL缓存依赖项,我们将在下一篇文章中继续讨论。
编程快乐!
作者简介
Scott Mitchell,本系列教程的作者,也是关于ASP/ASP的六本书的作者。NET,是4GuysFromRolla.com的创始人,自1998年以来一直使用微软的网络技术。你可以点击查看所有教程《[翻译]Scott Mitchell 的ASP.NET 2.0数据教程》,希望能帮助你学习ASP.NET。