[原创]Enterprise Library深入解析与灵活应用(2): 通过SqlDependency实现Cache和Database的同步...

对于一个真正的企业级的应用来说,Caching肯定是一个不得不考虑的因素,合理、有效地利用Caching对于增强应用的Performance(减少对基于Persistent storage的IO操作)、Scalability(将数据进行缓存,减轻了对Database等资源的压力)和Availability(将数据进行缓存,可以应对一定时间内的网络问题、Web Service不可访问问题、Database的崩溃问题等等)。Enterprise Library的Caching Application Block为我们提供了一个易用的、可扩展的实现Caching的框架。借助于Caching Application Block,Administrator和Developer很容易实现基于Caching的管理和编程。由于Caching的本质在于将相对稳定的数据常驻内存,以避免对Persistent storage的IO操作的IO操作,所以有两个棘手的问题:Load Balance问题;Persistent storage和内存中数据同步的问题。本篇文章提供了一个解决方案通过SqlDependency实现SQL Server中的数据和Cache同步的问题。

一、Cache Item的过期策略

在默认的情况下,通过CAB(以下对Caching Application Block的简称,注意不是Composite UI Application Block )的CacheManager加入的cache item是永不过期的;但是CacheManager允许你在添加cache item的时候通过一个ICacheItemExpiration对象应用不同的过期策略。CAB定了一个以下一些class实现了ICacheItemExpiration,以提供不同的过期方式:

  • AbsoluteTime:为cache item设置一个cache item的绝对过期时间。
  • ExtendedFormatTime:通过一个表达式实现这样的过期策略:每分钟过期(*****:5个*分别代表minute、hour、date、month、year);每个小时的第5分钟过期(5****);每个月的2号零点零分过期(0 0 2 * *)。
  • FileDependency:将cache item和一个file进行绑定,通过检测file的最后更新时间确定file自cache item被添加以来是否进行过更新。如果file已经更新则cache item过期。
  • NeverExpired:永不过期。
  • SlidingTime:一个滑动的时间,cache item的每次获取都将生命周期延长到设定的时间端,当cache item最后一次获取的时间算起,超出设定的时间,则cache item过期。

对于过期的cache item,会及时地被清理。所以要实现我们开篇提出的要求:实现Sql Server中的数据和Cache中的数据实现同步,我们可以通过创建基于Sql Server数据变化的cache item的过期策略。换句话说,和FileDependency,当Persistent storage(Database)的数据变化本检测到之后,对于得cache自动过期。但是,对于文件的修改和删除,我们和容易通过文件的最后更新日期或者是否存在来确定。对于Database中Table数据的变化的探测就不是那么简单了。不过SQL Server提供了一个SqlDependency的组建帮助我们很容易地实现了这样的功能。

二、创建基于SqlDependency的ICacheItemExpiration

SqlDependency是建立在SQL Server 2005的Service Broker之上。SqlDependency向SQL Server订阅一个Query Notification。当SQL Server检测到基于该Query的数据发生变化,向SqlDependency发送一个Notification,并触发SqlDependency的Changed事件,我们就可以通过改事件判断对应的cache item是否应该过期。

我们现在就来创建这样的一个ICacheItemExpiration。我们先看看ICacheItemExpiration的的定义:

   1: public interface ICacheItemExpiration
<!--CRLF-->
   2: {
<!--CRLF-->
   3:     // Methods
<!--CRLF-->
   4:     bool HasExpired();
<!--CRLF-->
   5:     void Initialize(CacheItem owningCacheItem);
<!--CRLF-->
   6:     void Notify();
<!--CRLF-->
   7: } 
<!--CRLF-->

而判断过期的依据就是根据HasExpired方法,我们自定义的CacheItemExpiration就是实现了该方法,根据SqlDependency判断cache item是否过期。下面是SqlDependencyExpiration的定义(注:SqlDependencyExpiration的实现通过Enterprise Library DAAB实现DA操作):

   1: namespace Artech.SqlDependencyCaching
<!--CRLF-->
   2: {
<!--CRLF-->
   3:     public class SqlDependencyExpiration : ICacheItemExpiration
<!--CRLF-->
   4:     {
<!--CRLF-->
   5:         private static readonly CommandType DefaultComamndType = CommandType.StoredProcedure; 
<!--CRLF-->
   6: 
<!--CRLF-->
   7:         public event EventHandler Expired; 
<!--CRLF-->
   8: 
<!--CRLF-->
   9:         public bool HasChanged
<!--CRLF-->
  10:         { get; set; } 
<!--CRLF-->
  11: 
<!--CRLF-->
  12:         public string ConnectionName
<!--CRLF-->
  13:         { get; set; } 
<!--CRLF-->
  14: 
<!--CRLF-->
  15:         public SqlDependencyExpiration(string commandText, IDictionary<string, object> parameters) :
<!--CRLF-->
  16:             this(commandText, DefaultComamndType, string.Empty, parameters)
<!--CRLF-->
  17:         { } 
<!--CRLF-->
  18: 
<!--CRLF-->
  19:         public SqlDependencyExpiration(string commandText, string connectionStringName, IDictionary<string, object> parameters) :
<!--CRLF-->
  20:             this(commandText, DefaultComamndType, connectionStringName, parameters)
<!--CRLF-->
  21:         { } 
<!--CRLF-->
  22: 
<!--CRLF-->
  23:         public SqlDependencyExpiration(string commandText, CommandType commandType, IDictionary<string, object> parameters) :
<!--CRLF-->
  24:             this(commandText, commandType, string.Empty, parameters)
<!--CRLF-->
  25:         { } 
<!--CRLF-->
  26: 
<!--CRLF-->
  27:         public SqlDependencyExpiration(string commandText, CommandType commandType, string connectionStringName, IDictionary<string, object> parameters)
<!--CRLF-->
  28:         {
<!--CRLF-->
  29:             if (string.IsNullOrEmpty(connectionStringName))
<!--CRLF-->
  30:             {
<!--CRLF-->
  31:                 this.ConnectionName = DatabaseSettings.GetDatabaseSettings(ConfigurationSourceFactory.Create()).DefaultDatabase;
<!--CRLF-->
  32:             }
<!--CRLF-->
  33:             else
<!--CRLF-->
  34:             {
<!--CRLF-->
  35:                 this.ConnectionName = connectionStringName;
<!--CRLF-->
  36:             } 
<!--CRLF-->
  37: 
<!--CRLF-->
  38:             SqlDependency.Start(ConfigurationManager.ConnectionStrings[this.ConnectionName].ConnectionString);
<!--CRLF-->
  39:             using (SqlConnection sqlConnection = DatabaseFactory.CreateDatabase(this.ConnectionName).CreateConnection() as SqlConnection)
<!--CRLF-->
  40:             {
<!--CRLF-->
  41:                 SqlCommand command = new SqlCommand(commandText, sqlConnection);
<!--CRLF-->
  42:                 command.CommandType = commandType;
<!--CRLF-->
  43:                 if (parameters != null)
<!--CRLF-->
  44:                 {
<!--CRLF-->
  45:                     this.AddParameters(command, parameters);
<!--CRLF-->
  46:                 }
<!--CRLF-->
  47:              SqlDependency dependency = new SqlDependency(command);
<!--CRLF-->
  48:                 dependency.OnChange += delegate
<!--CRLF-->
  49:                 {
<!--CRLF-->
  50:                     this.HasChanged = true;
<!--CRLF-->
  51:                     if (this.Expired != null)
<!--CRLF-->
  52:                     {
<!--CRLF-->
  53:                         this.Expired(this, new EventArgs());
<!--CRLF-->
  54:                     }
<!--CRLF-->
  55:                 };
<!--CRLF-->
  56:                 if (sqlConnection.State != ConnectionState.Open)
<!--CRLF-->
  57:                 {
<!--CRLF-->
  58:                     sqlConnection.Open();
<!--CRLF-->
  59:                 }
<!--CRLF-->
  60:                 command.ExecuteNonQuery();
<!--CRLF-->
  61:             }
<!--CRLF-->
  62:         } 
<!--CRLF-->
  63: 
<!--CRLF-->
  64:         private void AddParameters(SqlCommand command, IDictionary<string, object> parameters)
<!--CRLF-->
  65:         {
<!--CRLF-->
  66:             command.Parameters.Clear();
<!--CRLF-->
  67:             foreach (var parameter in parameters)
<!--CRLF-->
  68:             {
<!--CRLF-->
  69:                 string parameterName = parameter.Key;
<!--CRLF-->
  70:                 if (!parameter.Key.StartsWith("@"))
<!--CRLF-->
  71:                 {
<!--CRLF-->
  72:                     parameterName = "@" + parameterName;
<!--CRLF-->
  73:                 } 
<!--CRLF-->
  74: 
<!--CRLF-->
  75:                 command.Parameters.Add(new SqlParameter(parameterName, parameter.Value));
<!--CRLF-->
  76:             }
<!--CRLF-->
  77:         } 
<!--CRLF-->
  78: 
<!--CRLF-->
  79:         #region ICacheItemExpiration Members 
<!--CRLF-->
  80: 
<!--CRLF-->
  81:         public bool HasExpired()
<!--CRLF-->
  82:         {
<!--CRLF-->
  83:             bool indicator = this.HasChanged;
<!--CRLF-->
  84:             this.HasChanged = false;
<!--CRLF-->
  85:             return indicator;
<!--CRLF-->
  86:         } 
<!--CRLF-->
  87: 
<!--CRLF-->
  88:         public void Initialize(CacheItem owningCacheItem)
<!--CRLF-->
  89:         {         } 
<!--CRLF-->
  90: 
<!--CRLF-->
  91:         public void Notify()
<!--CRLF-->
  92:         {         } 
<!--CRLF-->
  93: 
<!--CRLF-->
  94:         #endregion
<!--CRLF-->
  95:     }
<!--CRLF-->
  96: } 
<!--CRLF-->
  97: 
<!--CRLF-->

我们来简单分析一下实现过程,先看看Property定义:

   1: private static readonly CommandType DefaultComamndType = CommandType.StoredProcedure; 
<!--CRLF-->
   2: 
<!--CRLF-->
   3: public event EventHandler Expired; 
<!--CRLF-->
   4: 
<!--CRLF-->
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值