[EntLib]微软企业库5.0 学习之路——第四步、使用缓存提高网站的性能(EntLib Caching)

      在前面的企业库学习之路里我分别使用了Data Access构建多数据库访问和使用Exception Handle+Logging记录系统的异常。今天我来介绍下企业库中的Caching模块在本项目中如何应用。

首先先补习下企业库的Caching Application Block的相关知识:

1、四大缓存方式,在Caching Application Block中,主要提供以下四种保存缓存数据的途径,分别是:内存存储(默认)、独立存储(Isolated Storage)、数据库存储(DataBase Cache Storage)和自定义存储(Custom Cache Storage)。

2、多种存储方式,抛开自定义存储不谈,内存存储是最基本的缓存,仅仅是将数据缓存到内存当中,虽然速度快但是无法进行持久化存储,而独立存储和数据库存储一个是存储到本地的磁盘中(视操作系统不同存储到不同的位置)而另一个则是存储到数据库中(方便进行分布式缓存),所以可以进行持久化保存不会因为关机而丢失(可以到。在EntLib50Src\Blocks\Caching\Src\Database\Scripts下找到脚本进行安装)

3、优秀的易用性,虽然在.NET类库System.Web中已经提供了Cache类,但是有局限性,仅可适用于控制台、Winform、Web、服务等。

4、安全性,企业库中的缓存模块可以和加密模块很好的结合起来,当适用数据库缓存、独立存储或者自定义存储的时候可以适用加密模块对缓存的数据进行加密,但存储到内存当中的数据就无法进行加密了。

 

在了解了缓存的基本知识后我们就可以开始进行具体的操作了。

我现在就是使用Cache模块为项目中反射具体数据库DAL层对象实例进行缓存,这样不用每次在调用底层的时候都反射一次,只需在第1次反射后缓存,以后的访问直接从缓存中读取,提高了访问的速度。

通过企业库配置工具添加个Caching Settings

pic17

这里使用默认设置,保存到内存中,过期轮询时间,最大存储数量和移除数量都使用了默认的设置。

如果不想使用默认的内存存储可以建立独立存储或者数据库存储。

这里有个要提的就是企业库的缓存模块的数据库存储是使用存储过程来进行缓存与数据库之间的交互,但是本项目中使用了多数据库,如Sqlite,就无法支持存储过程,所以这边需要自定义存储方式,可以直接查看企业库代码中Cache.DataBase.DataBackingStore.cs类,仿照DataBackingStore类自定义一个存储方式,只不过在进行数据库交互的时候使用SQL语句进行。

继续回到主题上,我这边写了一个简单的CacheHelper,用以操作缓存,其中我自定义了一个缓存刷新操作类(此类必须为可序列化),用于将已经过期的对象重新加入到缓存当中,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace EntLibStudy.Helper
{
    public static class CacheHelper
    {
        //2种建立CacheManager的方式
        //ICacheManager cache = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>();
        private static ICacheManager cache = CacheFactory.GetCacheManager();
        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="value">值</param>
        /// <param name="isRefresh">是否刷新</param>
        public static void Add(string key, object value, bool isRefresh = false)
        {
            if (isRefresh)
            {
                //自定义刷新方式,如果过期将自动重新加载,过期时间为5分钟
                cache.Add(key, value, CacheItemPriority.Normal, new MyCacheItemRefreshAction(), new AbsoluteTime(TimeSpan.FromMinutes(5)));
            }
            else
            {
                cache.Add(key, value);
            }
        }

        /// <summary>
        /// 获取缓存对象
        /// </summary>
        /// <param name="key">键</param>
        /// <returns></returns>
        public static object GetCache(string key)
        {
            return cache.GetData(key);
        }

        /// <summary>
        /// 移除缓存对象
        /// </summary>
        /// <param name="key">键</param>
        public static void RemoveCache(string key)
        {
            cache.Remove(key);
        }
    }

    /// <summary>
    /// 自定义缓存刷新操作
    /// </summary>
    [Serializable]
    public class MyCacheItemRefreshAction : ICacheItemRefreshAction
    {
        #region ICacheItemRefreshAction 成员
        /// <summary>
        /// 自定义刷新操作
        /// </summary>
        /// <param name="removedKey">移除的键</param>
        /// <param name="expiredValue">过期的值</param>
        /// <param name="removalReason">移除理由</param>
        void ICacheItemRefreshAction.Refresh(string removedKey, object expiredValue, CacheItemRemovedReason removalReason)
        {
            if (removalReason == CacheItemRemovedReason.Expired)
            {
                ICacheManager cache = CacheFactory.GetCacheManager();
                cache.Add(removedKey, expiredValue);
            }
        }
        #endregion
    }
}

1、缓存等级,在企业库的缓存模块中已经提供了4个缓存等级:LowNormalHighNotRemovable,在超出最大缓存数量后会自动根据缓存等级来移除对象。

2、ICacheItemRefreshAction,这个接口用来方便开发人员扩展使用的,开发人员可以根据移除原因在对象过期后进行相应的操作,其中CacheItemRemovedReason分

Expired:过期被移除

Removed:被手动移除

Scavenged:因为缓存数量已满,则根据缓存等级移除较低级的缓存

Unknown:未知移除,不建议使用

3、过期方式,企业库默认提供4种过期方式

AbsoluteTime:绝对是时间过期,传递一个时间对象指定到时过期

SlidingTime:缓存在最后一次访问之后多少时间后过期,默认为2分钟,有2个构造函数可以指定一个过期时间或指定一个过期时间和一个最后使用时间

public SlidingTime(TimeSpan slidingExpiration)
{
   // Check that expiration is a valid numeric value
   if (!(slidingExpiration.TotalSeconds >= 1))
   {
      throw new ArgumentOutOfRangeException("slidingExpiration",
                                       Resources.ExceptionRangeSlidingExpiration);
      }

      this.itemSlidingExpiration = slidingExpiration;
    }
public SlidingTime(TimeSpan slidingExpiration, DateTime originalTimeStamp) : this(slidingExpiration)
{
      timeLastUsed = originalTimeStamp;
 }  

ExtendedFormatTime :指定过期格式,以特定的格式来过期,通过ExtendedFormat.cs类来包装过期方式,具体可参照ExtendedFormat.cs,源代码中已经给出了很多方式

FileDependency:依赖于文件过期,当所依赖的文件被修改则过期,这个我觉得很有用,因为在许多网站,如论坛、新闻系统等都需要大量的配置,可以将配置文件信息进行缓存,将依赖项设为配置文件,这样当用户更改了配置文件后通过ICacheItemRefreshAction.Refresh可以自动重新缓存。

在介绍了Cache的相关参数后我们来看下具体如何使用,我这边将原来的DataAccess类重新修改了一下,因为觉得如果每次多增加一个数据表,对应的工厂就需要多写一个反射方法实在是不方便,所以修改成泛型类(同时附了原来的反射代码,可以对比下那种方式比较好),在BLL层调用的时候只需传递要转成的接口即可,代码如下:

public static class DataAccess<T>
    {
        private static readonly string assemblyString = ConfigurationManager.AppSettings["DAL"];

        /// <summary>
        /// 通用对象反射(包含缓存)
        /// </summary>
        /// <param name="className">要反射的类名</param>
        /// <returns></returns>
        public static T CreateObject(string className)
        {
            var typeName = assemblyString + "." + className;
            //判断对象是否被缓存,如果已经缓存则直接从缓存中读取,反之则直接反射并缓存
            var obj = (T)CacheHelper.GetCache(typeName);
            if (obj == null)
            {
                obj = (T)Assembly.Load(assemblyString).CreateInstance(typeName, true);
                CacheHelper.Add(typeName, obj, true);
            }
            return obj;
        }

        public static IClassInfoService CreateClassInfo()
        {
            string typeName = assemblyString + ".ClassInfoService";
            //判断对象是否被缓存,如果已经缓存则直接从缓存中读取,反之则直接反射并缓存
            if (CacheHelper.GetCache(typeName) != null)
            {
                return (IClassInfoService)CacheHelper.GetCache(typeName);
            }
            else
            {
                IClassInfoService service = (IClassInfoService)Assembly.Load(assemblyString).CreateInstance(typeName, true);
                CacheHelper.Add(typeName, service, true);
                return service;
            }
        }

BLL层调用代码如下:

private IClassInfoService classInfoService = DataAccess<IClassInfoService>.CreateObject("ClassInfoService");

需要注意的是由于使用企业库的Cache,如果缓存到数据库或者独立存储必须要求缓存对象必须是可序列化的,内存中缓存就不需要,而我这边缓存的对象为DAL层中具体的操作类,所以如果要更改为非内存存储需要将操作类加上[Serializable]特性。

这样以后再添加新的表就无需修改工厂中的DataAccess类了。

以上就是缓存在本项目中的一些基本应用,由于水平有限,所以暂时无法提出缓存的一些高级应用,请大家见谅。

相关Cache模块配置可以查看huangcong写的Cache模块(初级),一些相关知识可以查看virusswb写的缓存的设计目的

 

注意:

1、MSSQL数据库在DataBase目录下(需要自行附加数据库),SQLite数据库在Web目录的App_Data下,由于考虑到项目的大小,所以每个项目的BIN目录都已经删除,如出现无法生成项目请自行添加相关企业库的DLL。

2、由于微软企业库5.0 学习之路这个系列我是准备以一个小型项目的形式介绍企业库的各模块,所以源代码会根据系列文章的更新而更新,所以源代码不能保证与文章中所贴代码相同。

3、项目开发环境为:VS2010+SQL2005。

4、管理员帐户:admin

              密码:admin

源代码下载地址:点我下载

 

微软企业库5.0 学习之路系列文章索引:

第一步、基本入门

第二步、使用VS2010+Data Access模块建立多数据库项目

第三步、为项目加上异常处理(采用自定义扩展方式记录到数据库中) 

第四步、使用缓存提高网站的性能(EntLib Caching)

第五步、介绍EntLib.Validation模块信息、验证器的实现层级及内置的各种验证器的使用方法——上篇第五步、介绍EntLib.Validation模块信息、验证器的实现层级及内置的各种验证器的使用方法——中篇 

第五步、介绍EntLib.Validation模块信息、验证器的实现层级及内置的各种验证器的使用方法——下篇

第六步、使用Validation模块进行服务器端数据验证

第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇

第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—下篇

第八步、使用Configuration Setting模块等多种方式分类管理企业库配置信息

第九步、使用PolicyInjection模块进行AOP—PART1——基本使用介绍

第九步、使用PolicyInjection模块进行AOP—PART2——自定义Matching Rule

第九步、使用PolicyInjection模块进行AOP—PART3——内置Call Handler介绍

第九步、使用PolicyInjection模块进行AOP—PART4——建立自定义Call Handler实现用户操作日志记录 

第十步、使用Unity解耦你的系统—PART1——为什么要使用Unity?

第十步、使用Unity解耦你的系统—PART2——了解Unity的使用方法(1)

第十步、使用Unity解耦你的系统—PART2——了解Unity的使用方法(2)

第十步、使用Unity解耦你的系统—PART2——了解Unity的使用方法(3)

第十步、使用Unity解耦你的系统—PART3——依赖注入

第十步、使用Unity解耦你的系统—PART4——Unity&PIAB

扩展学习:

扩展学习篇、库中的依赖关系注入(重构 Microsoft Enterprise Library)[转]

 

posted @ 2010-06-24 12:00 kyo-yo 阅读(...) 评论(...) 编辑 收藏