ASP.NET的缓存
来源:互联网 发布:在太空看不到长城知乎 编辑:程序博客网 时间:2024/04/29 16:57
http://blog.csdn.net/takeie/archive/
1、 ASP.NET提供两种基本的缓存机制提供缓存功能。一是应用程序缓存,另一种是页输出缓存
1.1 应用程序缓存的使用
using System.Web.Caching
//添加数据项到应用程序缓存中
Cache["First"] = "1st Item";
Cache.Insert("Second", "2nd Item");
//为应用程序添加依赖项
string[] dependencies = { "Second" };
Cache.Insert("Third", "3rd Item", new CacheDependency(null, dependencies));
//手动移除相关数据项
Cache.Remove("First");
1.2 页输出缓存,分为整页缓存和部分页缓存
主要包含两个参数 Duration:用于设置页面或控件进行缓存的时间(秒)
VaryByParam:可以根据设置的参数值建立不同的缓存
//为TextBox1控件建立缓存,过期时间为60秒
<%@ OutputCache Duration="60" VaryByParam="TextBox1" %>
缓存机制的引用可以提高3倍多的性能,但缓存的数据过期问题在某些场景中甚至比低效的性能能更让人难以接受。
2. .Net 2.0 中引入自定义缓存依赖项,特别针对于SQL SERVER的SqlCacheDependency,可以避免数据过期的问题,它能够根据数据库中相应数据的变化,通知缓存
,并移除过期数据。同样,SqlCacheDependency也在System.Web.Caching命名空间下。
SQL SERVER 2005默认就支持SqlCacheDependency
SQL SERVER 2000要支持就必须进行相关配置,两种方法使用aspnet_regsql命令行工具,或者使用SqlCacheDependencyAdmin类。
2.1 使用aspnet_regsql
命令参数说明:
-? 显示该工具的帮助功能;
-S 后接的参数为数据库服务器的名称或者IP地址;
-U 后接的参数为数据库的登陆用户名;
-P 后接的参数为数据库的登陆密码;
-E 当使用windows集成验证时,使用该功能;
-d 后接参数为对哪一个数据库采用SqlCacheDependency功能;
-t 后接参数为对哪一个表采用SqlCacheDependency功能;
-ed 允许对数据库使用SqlCacheDependency功能;
-dd 禁止对数据库采用SqlCacheDependency功能;
-et 允许对数据表采用SqlCacheDependency功能;
-dt 禁止对数据表采用SqlCacheDependency功能;
-lt 列出当前数据库中有哪些表已经采用sqlcachedependency功能。
例如在Visual Studio 2005 命令提示输入aspnet_regsql -S localhost -E -d Test1-ed
说明将对名为Test1的数据库采用SqlCacheDependency功能,且SQL Server采用了windows集成验证方式.
输入完后,会自动在Test1数据库中建立名为AspNet_SqlCacheTablesForChangeNotification的表.
字段tableName记录要追踪的数据表的名称。
notificationCreated字段记录开始追踪的时间。
changeId作为一个类型为int的字段,用于记录数据表数据发生变化的次数
输入aspnet_regsql -S localhost -E -d Test1 -t TestMain -et表示对TestMain
除此之外,执行该命令还会为Test1数据库添加一组存储过程,为ASP.NET提供查询追踪的数据表的情况,同时还将为使用了SqlCacheDependency的表添加触发器,分别对应Insert、Update、Delete等与数据更改相关的操作。
如Test1表的触发器
Create TRIGGER dbo.[TestMain_AspNet_SqlCacheNotification_Trigger] ON [TestMain]
FOR INSERT, UPDATE, DELETE AS BEGIN
SET NOCOUNT ON
EXEC dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure N'TestMain'
END
其中,AspNet_SqlCacheUpdateChangeIdStoredProcedure即是工具添加的一组存储过程中的一个。当对Test1数据表执行Insert、Update或Delete等操作时,就会激活触发器,然后执行AspNet_SqlCacheUpdateChangeIdStoredProcedure存储过程。其执行的过程就是修改AspNet_SqlCacheTablesForChangeNotification数据表的changeId字段值
2.2 利用SqlCacheDependencyAdmin类
我们也可以利用编程的方式来来管理数据库对SqlCacheDependency特性的使用。该类包含了五个重要的方法:
DisableNotifications
为特定数据库禁用 SqlCacheDependency对象更改通知
DisableTableForNotifications
为数据库中的特定表禁用SqlCacheDependency对象更改通知
EnableNotifications
为特定数据库启用SqlCacheDependency对象更改通知
EnableTableForNotifications
为数据库中的特定表启用SqlCacheDependency对象更改通知
GetTablesEnabledForNotifications
返回启用了SqlCacheDependency对象更改通知的所有表的列表
假设我们定义了如下的数据库连接字符串:
const string connectionStr = "Server=localhost;Database=Test1";那么为数据库Test1启用SqlCacheDependency对象更改通知的实现为:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
SqlCacheDependencyAdmin.EnableNotifications(connectionStr);
}
}
为数据表TestMain启用SqlCacheDependency对象更改通知的实现则为:
SqlCacheDependencyAdmin.EnableTableForNotifications(connectionStr, "TestMain");
虽然说编程方式赋予了程序员更大的灵活性,但aspnet_regsql工具却提供了更简单的方法实现对SqlCacheDependency的配置与管理。PetShop 4.0采用的正是aspnet_regsql工具的办法,它编写了一个文件名为InstallDatabases.cmd的批处理文件,其中包含了对aspnet_regsql工具的执行,并通过安装程序去调用该文件,实现对SQL Server的配置。
3. 在PetShop 4.0中ASP.NET缓存的实现
3.1 CacheDependency接口
PetShop 4.0引入了SqlCacheDependency特性,对Category、Product和Item数据表对应的缓存实施了SQL Cache Invalidation技术。当对应的数据表数据发生更改后,该技术能够将相关项从缓存中移除。实现这一技术的核心是SqlCacheDependency类,它继承了CacheDependency类。然而为了保证整个架构的可扩展性,我们也允许设计者建立自定义的CacheDependency类,用以扩展缓存依赖。这就有必要为CacheDependency建立抽象接口,并在web.config文件中进行配置。
在PetShop 4.0的命名空间PetShop.ICacheDependency中,定义了名为IPetShopCacheDependency接口,它仅包含了一个接口方法:
public interface IpetShopCacheDependency
{
AggregateCacheDependency GetDependency();
}
AggregateCacheDependency是.Net Framework 2.0新增的一个类,它负责监视依赖项对象的集合。当这个集合中的任意一个依赖项对象发生改变时,该依赖项对象对应的缓存对象都将被自动移除。
AggregateCacheDependency类起到了组合CacheDependency对象的作用,它可以将多个CacheDependency对象甚至于不同类型的CacheDependency对象与缓存项建立关联。由于PetShop需要为Category、Product和Item数据表建立依赖项,因而IPetShopCacheDependency的接口方法GetDependency()其目的就是返回建立了这些依赖项的AggregateCacheDependency对象。
3.2 CacheDependency实现
CacheDependency的实现正是为Category、Product和Item数据表建立了对应的SqlCacheDependency类型的依赖项,如代码所示:
public abstract class TableDependency : PetShop.ICacheDependency.IPetShopCacheDependency {
protected char[] configurationSeparator = new char[] { ',' };
protected AggregateCacheDependency dependency = new AggregateCacheDependency();
protected TableDependency(string configKey) {
string dbName = ConfigurationManager.AppSettings["CacheDatabaseName"];
string tableConfig = ConfigurationManager.AppSettings[configKey];
string[] tables = tableConfig.Split(configurationSeparator);
foreach (string tableName in tables)
dependency.Add(new SqlCacheDependency(dbName, tableName));
}
public AggregateCacheDependency GetDependency()
{
return dependency;
}
需要建立依赖项的数据库与数据表都配置在web.config文件中,其设置如下:
<add key="CacheDatabaseName" value="MSPetShop4"/>
<add key="CategoryTableDependency" value="Category"/>
<add key="ProductTableDependency" value="Product,Category"/>
<add key="ItemTableDependency" value="Product,Category,Item"/>
根据各个数据表间的依赖关系,因而不同的数据表需要建立的依赖项也是不相同的,从配置文件中的value值可以看出。然而不管建立依赖项的多寡,其创建的行为逻辑都是相似的,因而在设计时,抽象了一个共同的类TableDependency,并通过建立带参数的构造函数,完成对依赖项的建立。由于接口方法GetDependency()的实现中,返回的对象dependency是在受保护的构造函数创建的,因此这里的实现方式也可以看作是Template Method模式的灵活运用。例如TableDependency的子类Product,就是利用父类的构造函数建立了Product、Category数据表的SqlCacheDependency依赖:
public class Product : TableDependency
{
public Product() : base("ProductTableDependency") { }
}
如果需要自定义CacheDependency,那么创建依赖项的方式又有不同。然而不管是创建SqlCacheDependency对象,还是自定义的CacheDependency对象,都是将这些依赖项添加到AggregateCacheDependency类中,因而我们也可以为自定义CacheDependency建立专门的类,只要实现IPetShopCacheDependency接口即可。
3.3 CacheDependency工厂
继承了抽象类TableDependency的Product、Category和Item类均需要在调用时创建各自的对象。由于它们的父类TableDependency实现了接口IPetShopCacheDependency,因而它们也间接实现了IPetShopCacheDependency接口,这为实现工厂模式提供了前提。
在PetShop 4.0中,依然利用了配置文件和反射技术来实现工厂模式。命名空间PetShop.CacheDependencyFactory中,类DependencyAccess即为创建IPetShopCacheDependency对象的工厂类:
public static class DependencyAccess
{
public static IPetShopCacheDependency CreateCategoryDependency()
{
return LoadInstance("Category");
}
public static IPetShopCacheDependency CreateProductDependency()
{
return LoadInstance("Product");
}
public static IPetShopCacheDependency CreateItemDependency()
{
return LoadInstance("Item");
}
private static IPetShopCacheDependency LoadInstance(string className)
{
string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"];
string fullyQualifiedClass = path + "." + className;
return (IPetShopCacheDependency)Assembly.Load(path).CreateInstance(fullyQualifiedClass);
}
}
虽然DependencyAccess类创建了实现了IPetShopCacheDependency接口的类Category、Product、Item,然而我们之所以引入IPetShopCacheDependency接口,其目的就在于获得创建了依赖项的AggregateCacheDependency类型的对象。我们可以调用对象的接口方法GetDependency(),如下所示:
AggregateCacheDependency dependency = DependencyFacade.GetCategoryDependency();
比起直接调用DependencyAccess类的GetDependency()方法而言,除了方法更简单之外,同时它还对CacheDependencyAssembly配置节进行了判断,如果其值为空,则返回null对象。
在PetShop.Web的App_Code文件夹下,静态类WebUtility的GetCategoryName()和GetProductName()方法调用了DependencyFacade类。例如GetCategoryName()方法:
public static string GetCategoryName(string categoryId)
{
Category category = new Category();
if (!enableCaching)
return category.GetCategory(categoryId).Name;
string cacheKey = string.Format(CATEGORY_NAME_KEY, categoryId);
// 检查缓存中是否存在该数据项;
string data = (string)HttpRuntime.Cache[cacheKey];
if (data == null)
{
// 通过web.config的配置获取duration值;
int cacheDuration = int.Parse(ConfigurationManager.AppSettings["CategoryCacheDuration"]);
// 如果缓存中不存在该数据项,则通过业务逻辑层访问数据库获取;
data = category.GetCategory(categoryId).Name;
// 通过Facade类创建AggregateCacheDependency对象;
AggregateCacheDependency cd = DependencyFacade.GetCategoryDependency();
// 将数据项以及AggregateCacheDependency 对象存储到缓存中;
HttpRuntime.Cache.Add(cacheKey, data, cd, DateTime.Now.AddHours(cacheDuration), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
}
return data;
}
GetCategoryName()方法首先会检查缓存中是否已经存在CategoryName数据项,如果已经存在,就通过缓存直接获取数据;否则将通过业务逻辑层调用数据访问层访问数据库获得CategoryName,在获得了CategoryName后,会将新获取的数据连同DependencyFacade类创建的AggregateCacheDependency对象添加到缓存中。
WebUtility静态类被表示层的许多页面所调用,例如Product页面:
public partial class Products : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//get page header and title
Page.Title = WebUtility.GetCategoryName(Request.QueryString["categoryId"]);
}
}
显示页面title的逻辑是放在Page_Load事件方法中,因而每次打开该页面都要执行获取CategoryName的方法。如果没有采用缓存机制,当Category数据较多时,页面的显示就会非常缓慢。
3.3 代理模式
业务逻辑层BLL中与Product、Category、Item有关的业务方法,其实现逻辑是调用数据访问层(DAL)对象访问数据库,以获取相关数据。为了改善系统性能,我们就需要为这些实现方法增加缓存机制的逻辑。当我们操作增加了缓存机制的业务对象时,对于调用者而言,应与BLL业务对象的调用保持一致。也即是说,我们需要引入一个新的对象去控制原来的BLL业务对象,这个新的对象就是Proxy模式中的代理对象。
public static class ProductDataProxy
{
private static readonly int productTimeout = int.Parse(ConfigurationManager.AppSettings["ProductCacheDuration"]);
private static readonly bool enableCaching = bool.Parse(ConfigurationManager.AppSettings["EnableCaching"]);
public static ProductInfo GetProduct(string productId)
{
Product product = new Product();
if (!enableCaching)
return product.GetProduct(productId);
string key = "product_" + productId;
ProductInfo data = (ProductInfo)HttpRuntime.Cache[key];
// 检查数据是否在缓存里
if (data == null)
{
//如果不存在从业务逻辑层里取出数据
data = product.GetProduct(productId);
// 从工厂中创建一个AggregateCacheDependency对象
AggregateCacheDependency cd = DependencyFacade.GetProductDependency();
// 存入缓存,并创建一个AggregateCacheDependency对象 HttpRuntime.Cache.Add(key, data, cd, DateTime.Now.AddHours(productTimeout), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
}
return data;
}
}
引入Proxy模式,实现了在缓存级别上对业务对象的封装,增强了对业务对象的控制。由于暴露在对象外的方法是一致的,因而对于调用方而言,调用代理对象与真实对象并没有实质的区别。
如果需要对UI层采用缓存机制,将应用程序数据存放到缓存中,就可以调用这些代理对象。以ProductsControl用户控件为例,调用方式如下:
productsList.DataSource = ProductDataProxy.GetProductsByCategory(categoryKey);
PetShop 4.0的设计中,对于类似于ProductsControl类型的控件而言,采用的缓存机制是页输出缓存。我们可以从ProductsControl.ascx页面的Source代码中发现
<%@ OutputCache Duration="100000" VaryByParam="page;categoryId" %>
与ASP.NET 1.x的页输出缓存不同的是,在ASP.NET 2.0中,为ASP.NET用户控件新引入了CachePolicy属性,该属性的类型为ControlCachePolicy类,它以编程方式实现了对ASP.NET用户控件的输出缓存设置。我们可以通过设置ControlCachePolicy类的Dependency属性,来设置与该用户控件相关的依赖项,例如在ProductsControl用户控件中,进行如下的设置:
protected void Page_Load(object sender, EventArgs e)
{
this.CachePolicy.Dependency = DependencyFacade.GetProductDependency();
}
采用页输出缓存,并且利用ControlCachePolicy设置输出缓存,能够将业务数据与整个页面放入到缓存中。这种方式比起应用程序缓存而言,在性能上有很大的提高。同时,它又通过引入的SqlCacheDependency特性有效地避免了“数据过期”的缺点,因而在PetShop 4.0中被广泛采用。相反,之前为Product、Category、Item业务对象建立的代理对象则被“投闲散置”,仅仅作为一种设计方法的展示而“幸存”与整个系统的源代码中。
- asp.net的缓存
- ASP.NET的缓存
- ASP.NET的缓存
- ASP.NET的缓存
- asp.net的缓存
- ASP.NET的缓存
- ASP.NET的缓存技术
- Asp.net的局部缓存
- ASP.NET的缓存技术
- ASP.NET的输出缓存
- ASP.NET的输出缓存
- asp.net的缓存机制
- ASP.NET的缓存机制
- ASP.NET的缓存机制
- ASP.NET的缓存详解
- ASP.NET 缓存的使用
- ASP.NET 2.0的缓存技术简介 [Asp.Net]
- 关于ASP.NET的缓存的详细内容
- 嵌套
- 单输出感知器学习算法实现修改
- Apache跨平台解决方案
- 《天龙八部》之《少年游》
- 达内免费Java训练 第七天
- ASP.NET的缓存
- 24值得纪念的日子
- 日本的惊人秘密,中国人必看
- [C]百鸡问题
- 如果我是老板
- 《天龙八部》之《苏幕遮》
- 关于考研的问题?想听听大家的意见.
- 出差在福建
- C#操作EXCEL