缓存ASP_net mvc 自定义的OutputCache

outputcache  时间:2021-04-18  阅读:()

在ASP.NET中构建和使用自定义的

OutputCache提供程序

Brandon Satrom

下载代码示例

如果您是一位Web开发人员您过去可能使用过ASP.NET提供的输出缓存功能。 ASP.NET输出缓存功能是随着Microsoft .NET Framework的第一个版本推出的该功能通过从缓存中检索向站点访问者提供的内容以及避免重新执行页面或控制器提高了向站点访问者提供内容方面的性能。 当返回您不经常更新的数据或者返回一段时间后将过期的数据时该功能不需要您的应用程序执行耗费大量资源的数据库调用操作。

ASP.NET输出缓存使用的是内存存储机制并且在NET Framework 4出现之前您无法使用您自己的实现覆盖或替代默认缓存。 现在借助新的OutputCacheProvider类型您可以在ASP.NET中实现您自己的缓存页面输出机制。

在本文中我将为您介绍两种自定义机制。 首先我将使用MongoDB 一种常用的面向文档数据库在一个简单的ASP.NET MVC应用程序中创建我自己的提供程序以方便输出缓存。然后我会使用同一个应用程序快速交换我的自定义提供程序 以便利用Windows AzureAppFabric的功能—具体地说就是在云中利用Windows Azure基础结构提供分布式内存缓存的新DistributedCache提供程序。

ASP.NET中的输出缓存

在ASP.NET Web窗体应用程序中可以通过向任意ASP.NET页面或用户控件添加

OutputCache Page指令来配置输出缓存

1. <%@ OutputCache Duration="60" Location="Any" VaryByParam="name"

%>

2.

对于ASP.NET MVC应用程序输出缓存是使用ASP.NET MVC附带的操作筛选器来提供的该操作筛选器可用作任何控制器操作的一个属性

1. [OutputCache(Duration=60, VaryByParam="none") ]

2.

“Duration”和“VaryByParam”在ASP.NET MVC 1和2应用程序中是必需的VaryByParam在ASP.NET MVC 3中是可选的 这两种机制都提供其他一些属性和参数这些属性和参数使开发人员能够控制缓存内容的方式一些VaryByX参数 、缓存内容的位置(Location)和用于设置缓存无效依赖项的功能(SqlDependency) 。

对于传统的输出缓存在您的应用程序中实现该功能时不需要任何其他东西。 OutputCache类型是一个在您的应用程序启动时运行并在遇到页面指令或操作筛选器时开始发挥作用的HttpModule。 收到第一个相关的页面或控制器请求后 ASP.NET将接收生成的内容HTML、

CSS、JavaScript文件等并将各个项目以及过期日期和用于标识相应项目的关键字放入内存缓存中。 过期日期由Duration属性确定关键字则由到页面的路径和必要的VaryBy值的组合确定—例如如果提供了VaryByParam属性则会查询字符串或参数值。 现在请考虑一下以这种方式定义的控制器操作

1. [OutputCache(Duration=20, VaryByParam="vendorState") ]

2. Public ActionResult GetVendorList(string vendorState)

3. {

4. // Action logic here.

5. }

6.

在这种情况下对于vendorState的各个实例例如一个针对德克萨斯州一个针对华盛顿州等等  ASP.NET将在请求该州时分别缓存生成的HTML视图的一个实例。 在这种情况下存储各个实例所使用的关键字将是相关路径和vendorState的组合。

另一方面如果将VaryByParam属性设置为“none” 则ASP.NET将缓存第一次执行GetVendorList的结果并且会向所有后续请求传递相同的缓存版本而不考虑vendorState参数的值是否传入了相应操作。当没有提供VaryByParam值时存储此实例所使用的关键字就是路径。 图1简单描述了此过程。

图1 ASP.NET输出缓存过程

除了用于控制缓存中的项目的生存期的Duration参数以外还有一些VaryBy参数

VaryByParam、 VaryByHeader、 VaryByCustom、 VaryByControl和VaryByContentEncoding用于控制缓存项目的精度可以配置输出缓存以控制缓存内容的位置客户端、服务器或下游代理服务器 。 此外 ASP.NET 2.0引入了一个SqlDependency属性该属性允许开发人员指定页面或控件所依赖的数据库表因此除了在时间上过期以外您的基础源数据的更新也可能会导致缓存项目过期。

尽管.NET Framework 2.0和3.0向默认缓存提供程序中引入了一些增强功能但是提供程序本身仍然没有改变它仍然是一种内存存储并没有用于为您提供您自己的实现的扩展点或方法。 在大多数情况下 内存缓存是一种完全可以接受的方法但有时候 当服务器资源透支且内存不足时它也会削弱站点性能。 此外 当内存确实不足时会导致开发人员无法有效地控制管理缓存资源的方式从而导致默认缓存提供程序机制自动弃用缓存的资源不考虑指定的持续时间 。

ASP.NET中可扩展的输出缓存

.NET Framework 4引入了一种新功能该功能使开发人员能够创建他们自己的输出缓存提供程序并且只需对新的或现有的应用程序及其配置作少量更改便可轻松地将这些提供程序插入其中。 无论他们选择的缓存信息使用的是哪种存储机制例如本地磁盘、相关和非相关数据库、云甚至是分布式缓存引擎如Windows Server AppFabric中所提供的  都可以随意使用这些提供程序。 甚至还可以针对相同应用程序中的不同页面使用多个提供程序。与创建派生自新的System.Web.Caching.OutputCacheProvider抽象类的新类以及覆盖ASP.NET使用缓存项目所需的四种方法一样创建您自己的输出缓存提供程序也很简单。 下面列出了OutputCacheProvider类的框架定义请参见bit. ly/fozTLc以了解更多信息 

1. public abstract class OutputCacheProvider : ProviderBase

2. {

3. public abstract object Get(string key) ;

4. public abstract object Add(string key, object entry, DateTime utcExpiry

) ;

5. public abstract void Set(string key, object entry, DateTime utcExpiry) ;

6. public abstract void Remove(string key) ;

7. }

8.

实现了这四种方法后接下来要做的就是向您的web.config添加新提供程序将其指定为默认提供程序并向您的应用程序添加一个OutputCache指令或属性。 我会在介绍如何创建我们自己的输出缓存提供程序使用一个叫做MongoDB的文档数据库时详细说明这些步骤。首先我要介绍一些关于在构建自定义提供程序时使用的工具的背景信息这可能对大家有所帮助。

NoSQL、文档数据库和MongoDB

在过去的数十年中首选应用程序存储机制一直都是关系数据库管理系统(RDBMS) 该系统在表中存储数据和关系。 SQL Server和Oracle都属于RDBMS它们也是目前最受欢迎的商业和开源数据库。

然而并非所有需要存储的问题都适合相同的事务建模。二十世纪九十年代末随着Internet的扩展许多站点都扩大了规模以便托管大量数据很明显关系模型在某些类型的数据密集型应用程序上的性能差强人意。 例如索引大量文档、在高流量站点上向消费者传递网页或向其传递流媒体。

许多公司通过转而使用NoSQL数据库解决了他们不断增加的存储需求 NoSQL数据库是一种不公开SQL接口、 固定架构或预定义关系的轻型数据库。 NoSQL数据库得到了企业例如Google Inc. (BigTable) 、 Amazon.com Inc. (Dynamo)和Facebook 其收件箱搜索的存储量超过了50TB 的广泛使用并且在普及性和使用方面实现了稳定增长。

值得注意的是虽然有些人将NoSQL这个词用作了呼吁停用所有RDBMS的口号但另外一些人则强调同时利用这两类存储的价值。 人们构建NoSQL数据库的目的是解决RDBMS无法解决的一类问题而不是要用它彻底代替这些系统。 有辨别能力的开发人员应该会清楚地了解这两种系统并根据具体情况来利用相应的系统有时甚至会在一个应用程序中混合使用这两种类型的存储。

非常适合使用NoSQL数据库的一种情况就是输出缓存。 NoSQL数据库对于使用瞬态数据或临时数据来说是理想之选而从ASP.NET应用程序缓存的页面当然符合要求。 MongoDB(mongodb.org)是一个常用的NoSQL选项 Shutterfly、 Foursquare、 《纽约时报》等许多公司都在使用这种面向文档的NoSQL数据库。 MongoDB是一个以C++编写的完全开放的源数据库该数据库具有面向几乎所有主要编程语言包括C#的驱动程序。 我们将会把MongoDB用作我们的自定义输出缓存提供程序的存储机制。

使用MongoDB构建自定义的OutputCacheProvider

要开始使用该工具您需要登录mongodb.org下载和安装该工具。mongodb.org/display/DOCS/Quickstart上提供的文档中包含了在Windows、 Mac OS X和Unix上安装MongoDB时所需了解的所有信息。 下载MongoDB并使用外壳程序完成测试后我建议您使用安装目录中的以下命令来将该数据库作为一项服务进行安装请务必以管理员身份运行cmd.exe 

C:\Tools\MongoDB\bin>mongod.exe --logpath C:\Tools\MongoDB\Logs--directoryperdb --install

MongoDB将作为一项服务自行安装到您的计算机上并将使用C:\Data\db作为其所有数据库的默认目录。 通过diretoryperdb选项可让MongoDB为您创建的每个数据库创建根目录。运行前一个命令之后键入以下内容以启动服务net start MongoDB

启动并运行MongoDB后您需要在.NET中安装一个驱动程序库以使用MongoDB。 有几个选项可供使用我将使用Sam Corder创建的mongodb-csharp驱动程序

(github.com/samus/mongodb-csharp) 。

我们安装了MongoDB并且在.NET应用程序中有一个可以使用的驱动程序因此现在是时候创建自定义的输出缓存提供程序了。 为了执行这项操作我创建了一个叫做DocumentCache的新类库并添加了两个类 DocumentDatabaseOutputCacheProvider和CacheItem。第一个是我的提供程序它是一个可对抽象OutputCacheProvider进行子类化的公共类。 开始的实现过程如图2所示。

图2起始OutputCacheProvider类

1. public class DocumentDatabaseOutputCacheProvider : OutputCacheProvider

2. {

3. readonly Mongo _mongo;

4. readonly IMongoCollection<CacheItem> _cacheItems;

5.

6. public override object Get(string key)

7. {

8. return null;

9. }

10.

11. public override object Add(string key, object entry, DateTime utcExpiry

)

12. {

13. return null;

14. }

15.

16. public override void Set(string key, object entry, DateTime utcExpiry)

17. {

18. return;

19. }

20.

21. public override void Remove(string key)

22. {

23. return;

24. }

25. }

26.

请注意图2中的第二个私有变量是指CacheItem即我需要在我的项目中创建的另一个类。CacheItem的作用是包含我的输出缓存提供程序与ASP.NET和我的数据库协同工作所需的相关详细信息不过它不是我的提供程序所需的外部对象。 因此我将CacheItem定义为内部类如下所示

1. [Serializable]

2. internal class CacheItem

3. {

4. public string Id { get; set; }

5. public byte[] Item { get; set; }

6. public DateTime Expiration { get; set; }

7. }

8.

Id映射到ASP.NET向我提供的关键字。 您会想到这个关键字是路径和在您的页面指令或操作属性中定义的任何VaryBy条件的组合。 Expiration字段对应于Duration参数而Item属性是要缓存的项目。

我们将通过在DocumentDatabaseOutputCacheProvider类的构造函数中进行一些设置来开始实现我们的提供程序。 由于我们知道ASP.NET在应用程序的整个生存期内仅保留提供程序的一个实例 因此我们可以在构造函数中进行一些设置如下所示

1. readonly Mongo _mongo;

2. readonly IMongoCollection<CacheItem> _cacheItems;

3.

4. public DocumentDatabaseOutputCacheProvider()

5. {

6. _mongo = new Mongo() ;

7. _mongo.Connect() ;

8.

9. var store = _mongo.GetDatabase("OutputCacheDB") ;

10. _cacheItems = store.GetCollection<CacheItem>() ;

11. }

12.

构造函数创建一个Mongo类型的新实例并使用默认位置(localhost)连接到服务器。 之后它向MongoDB请求OutputCacheDB数据库以及一个CacheItem类型的

IMongoCollection。 由于MongoDB是一个架构灵活的数据库因此支持动态创建数据库。第一次调用_mongo.GetDatabase( “OutputCacheDB” )将返回新数据库的一个实例 当第一次插入发生时便会在磁盘上创建该数据库。

现在让我们实现Add方法如图3所示。

图3实现Add方法

1. public override object Add(string key, object entry, DateTime utcExpiry)

2. {

3. key = MD5(key) ;

4. var item = _cacheItems.FindOne(new { _id = key } ) ;

5. if (item != null) {

6. if (item.Expiration.ToUniversalTime() <= DateTime.UtcNow) {

7. _cacheItems.Remove(item) ;

8. } else {

9. return Deserialize(item. Item) ;

10. }

11. }

12.

13. _cacheItems. Insert(new CacheItem

14. {

15. Id = key,

16. Item = Serialize(entry) ,

17. Expiration = utcExpiry

18. } ) ;

19.

20. return entry

21. }

22.

我在各个方法中执行的第一个操作就是对传入的关键字调用MD5方法。 此方法为了保持简洁此处未作详细说明但您可以从在线源代码下载部分中进行查看生成基于ASP.NET向我提供的关键字的、数据库可以识别的MD5哈希。 然后我调用我的

IMongoCollection<CacheItem>类型_cacheItems以便在底层数据库中查询那个关键字。请注意传入FindOne方法中的匿名类型(new {_id = key} ) 。 查询MongoDB主要是通过选择器对象或在文档中指定一个或多个字段以便在数据库中进行匹配的模板文档来完成的。 _id是MongoDB用于存储文档的关键字而按照我所使用的驱动程序的约定该属性会自动映射

到我的CacheItem类的Id属性。 因此 当我保存新的缓存项时正如您在图3中所示的_cacheItems. Insert方法中看到的关键字是使用Id属性进行分配的 MongoDB使用该属性填充记录的内部_id字段。 MongoDB是一种关键字值存储 因此各个CacheItem对象都是使用二进制序列化的JSON进行存储的如下所示

1. { "_id" : ObjectId(Id) , "CacheItem": new CacheItem { Id = key,

Item = entry, Expiration = utcExpiry } }

2.

如果我发现一个CacheItem具有与传入的关键字相同的关键字则我会根据当前的UTC时间来检查该项目是否过期。 如果该项目尚未过期则我会利用私有方法可在在线源代码部分中找到该方法对它进行二进制反序列化并返回现有项目。 此外我向我的存储中插入一个新项目对其进行二进制序列化并返回传入的条目。

向缓存中添加了项目后我便可以添加Get方法该方法将根据关键字查找并返回一个缓存项如果找不到结果则返回nul l 如图4所示。

图4实现Get方法

1. public override object Get(string key)

2. {

3. key = MD5(key) ;

4. var cacheItem = _cacheItems.FindOne(new { _id = key } ) ;

5.

6. if (cacheItem != null) {

7. if (cacheItem.Expiration.ToUniversalTime() <= DateTime.UtcNow) {

8. _cacheItems.Remove(cacheItem) ;

9. } else {

10. return Deserialize(cacheItem. Item) ;

11. }

12. }

13.

14. return null;

15. }

16.

与Add方法一样 Get方法也会检查数据库中存在的项目是否过期如果项目已经过期则会将其删除并返回null。 如果存在的项目尚未过期则会返回该项目。

现在让我们来实现Remove方法该方法可接受一个关键字并从数据库中删除与该关键字相匹配的项目如下所示

1. public override void Remove(string key)

2. {

3. key = MD5(key) ;

4. _cacheItems.Remove(new { _id = key } ) ;

5. }

6.

正如我们的驱动程序为了获得尚不存在的数据库而使用的代码一样如果我们尝试删除数据库中不存在的项目 MongoDB不会报错。 它不会执行任何操作。

根据我们的抽象基类我们仍需要实现最后一个方法—Set方法来获得一个功能性自定义输出缓存提供程序。 我已在图5中使用了该方法。

图5实现Set方法Public Override Void Set(string key, object entry, DateTimeutcExpiry)

1. {

2. key = MD5(key) ;

3. var item = _cacheItems.FindOne(new { _id = key } ) ;

4.

5. if (item != null)

6. {

7. item. Item = Serialize(entry) ;

8. item.Expiration = utcExpiry;

9. _cacheItems.Save(item) ;

10. }

11. else

12. {

13. _cacheItems. Insert(new CacheItem

14. {

15. Id = key,

16. Item = Serialize(entry) ,

17. Expiration = utcExpiry

18. } ) ;

19. }

20. }

21.

总体来看 Add方法似乎与Set方法是相同的但它们的预期实现之间却存在重要区别。 根据关于OutputCacheProvider类的MSDN库文档(bit. ly/fozTLc)  自定义提供程序的Add方法应在缓存中查找与指定关键字匹配的值如果存在这样的值则不对缓存执行任何操作并返回保存的项目。 如果不存在该项目 Add应插入该项目。

另一方面 Set方法应始终将其值保存在缓存中如果不存在该项目则插入该项目如果存在则覆盖该项目。 在关于Add的图3和关于Set的图5中您会注意到这两种方法都是按指定方式执行的。

实现这四种方法后现在我们可以运行我们的提供程序了。

在ASP.NET MVC中使用MongoDB OutputCacheProvider编译完我们的自定义提供程序后我们可以通过几行配置将此提供程序添加到任何ASP.NET应用程序中。 添加对包含此提供程序的程序集的引用后将以下文本添加到您的web.config文件的<system.web>部分

<caching>

<outputCache defaultProvider="DocumentDBCache">

<providers>

<add name="DocumentDBCache"type="DocumentCache.DocumentDatabaseOutputCacheProvider, DocumentCache" />

</providers>

</outputCache>

</caching>

<providers>元素定义您想添加到应用程序中的所有自定义提供程序并为每个提供程序定义名称和类型。 由于您可以在一个应用程序中包含多个自定义提供程序 因此您还希望指定defaultProvider属性如我在之前的代码段中的操作一样。

我的示例应用程序是一个带有CustomersController的简单ASP.NET MVC站点。 该控制器中有一个称为TopCustomers的操作它返回我的业务的顶级客户列表。 此信息经过复杂计算并在我的SQL Server数据库中进行多次数据库查询得出且每小时仅更新一次。 因此它是缓存的理想候选项。 所以我在我的操作中添加了一个OutputCache属性如下所示

1. [OutputCache(Duration = 3600, VaryByParam = "none") ]

2. public ActionResult TopCustomers()

3. {

4. var topCustomers = _repository.GetTopCustomers() ;

5. return View(topCustomers) ;

6. }

7.

现在如果我运行站点并导航到我的TopCustomers页面那么我的自定义提供程序便会执行。首先将调用我的Get方法但是由于此页面还未缓存 因此不会返回任何内容。 接下来控制器操作将执行并返回TopCustomers视图如图6所示。

Boomer.Host(年付3.5美)休斯敦便宜VPS

Boomer.Host是一家比较新的国外主机商,虽然LEB自述 we’re now more than 2 year old,商家提供虚拟主机和VPS,其中VPS主机基于OpenVZ架构,数据中心为美国得克萨斯州休斯敦。目前,商家在LET发了两款特别促销套餐,年付最低3.5美元起,特别提醒:低价低配,且必须年付,请务必自行斟酌确定需求再入手。下面列出几款促销套餐的配置信息。CPU:1core内存:...

博鳌云¥799/月,香港110Mbps(含10M CN2)大带宽独立服务器/E3/8G内存/240G/500G SSD或1T HDD

博鳌云是一家以海外互联网基础业务为主的高新技术企业,运营全球高品质数据中心业务。自2008年开始为用户提供服务,距今11年,在国人商家中来说非常老牌。致力于为中国用户提供域名注册(国外接口)、免费虚拟主机、香港虚拟主机、VPS云主机和香港、台湾、马来西亚等地服务器租用服务,各类网络应用解決方案等领域的专业网络数据服务。商家支持支付宝、微信、银行转账等付款方式。目前香港有一款特价独立服务器正在促销,...

CloudCone2核KVM美国洛杉矶MC机房机房2.89美元/月,美国洛杉矶MC机房KVM虚拟架构2核1.5G内存1Gbps带宽,国外便宜美国VPS七月特价优惠

近日CloudCone发布了七月的特价便宜优惠VPS云服务器产品,KVM虚拟架构,性价比最高的为2核心1.5G内存1Gbps带宽5TB月流量,2.89美元/月,稳定性还是非常不错的,有需要国外便宜VPS云服务器的朋友可以关注一下。CloudCone怎么样?CloudCone服务器好不好?CloudCone值不值得购买?CloudCone是一家成立于2017年的美国服务器提供商,国外实力大厂,自己开...

outputcache为你推荐
伺服器chrome支持ipad支持ipad勒索病毒win7补丁我的电脑是windows7系统,为什么打不了针对勒索病毒的补丁(杀毒软件显iexplore.exe应用程序错误iexplore.exe - 应用程序错误怎么办阿??????联通版iphone4s怎么知道到苹果4s是联通版,还是移动版谷歌sb为什么搜索SB第一个是google?googleadsensegoogle adsense 和google adwords有什么区别?适合什么样的人群?firefoxflash插件火狐浏览器怎么安装flash迅雷下载速度为什么 迅雷下载速度太慢
合肥虚拟主机 google电话 duniu bluehost 免费主机 realvnc 好玩的桌面 韩国网名大全 php空间购买 网站在线扫描 域名与空间 英国伦敦 博客域名 贵州电信 标准机柜 qq空间打开很慢 日本小学生 天鹰抗ddos防火墙 电脑主机打不开 长沙服务器托管 更多