三、如何应用SqlDependencyExpiration我们现在创建一个简单的Windows Application来模拟使用我们创建的SqlDependencyExpiration。我们模拟一个简单的场景:假设我们有一个功能需要向系统所有的user发送通知,而且不同的user,通知是不一样的,由于通知的更新的频率不是很高,我们需要讲某个User的通知进行缓存。
这是我们的表结构:Messages

附件:
您所在的用户组无法下载或查看附件我们通过下面的SP来获取基于某个User 的Message:

Code
ALTER PROCEDURE [dbo].[Message_Select_By_User]
(@UserID VarChar(50))
AS
BEGIN
Select ID, UserID, [Message] From dbo.Messages Where UserID = @UserID
END
注:如何写成Select * From dbo.Messages Where UserID = @UserID, SqlDependency 将不能正常运行;同
时Table的schema(dbo)也是必须的。
我们设计如下的界面来模拟:通过Add按钮,可以为选择的User创建新的Message,而下面的List将显示基于某个User(Foo)的Message List。该列表的获取方式基于Lazy Loading的方式,如果在Cache中,则直接从Cache中获取,否则从Db中获取,并将获取的数据加入cache。

附件:
您所在的用户组无法下载或查看附件我们先定义了3个常量,分别表示:缓存message针对的User,获取Message list的stored procedure名称和Cache item的key。

Code
private const string UserName = "Foo";
private const string MessageCachingProcedure = "Message_Select_By_User";
private const string CacheKey = "__MessageOfFoo";
我们通过一个Property来创建或获取我们的上面定义的SqlDependencyExpiration 对象

Code
private SqlDependencyExpiration CacheItemExpiration
{
get
{
IDictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("UserID", UserName);
SqlDependencyExpiration expiration= new SqlDependencyExpiration(MessageCachingProcedure, parameters);
expiration.Expired += delegate
{
MessageBox.Show("Cache has expired!");
};
return expiration;
}
}
通过GetMessageByUser从数据库中获取基于某个User的Message List(使用了DAAB):

Code
private List<string> GetMessageByUser(string userName)
{
List<string> messageList = new List<string>();
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = db.GetStoredProcCommand(MessageCachingProcedure);
db.AddInParameter(command, "UserID", DbType.String, userName);
IDataReader reader = db.ExecuteReader(command);
while (reader.Read())
{
messageList.Add(reader.GetString(2));
}
return messageList;
}
通过GetMessages获取User(Foo)的Message List:首先通过CacheManager检测message list是否存在于Cache,如何不存在,调用上面的GetMessageByUser方法从database中获取Foo的message list。并将其加入Cache中,需要注意的是这里使用到了我们的SqlDependencyExpiration 对象。

Code
private List<string> GetMessages()
{
ICacheManager manager = CacheFactory.GetCacheManager();
if (manager.GetData(CacheKey) == null)
{
manager.Add(CacheKey, GetMessageByUser(UserName), CacheItemPriority.Normal, null, this.CacheItemExpiration);
}
return manager.GetData(CacheKey) as List<string>;
}
由于在我们的例子中需要对DB进行数据操作,来检测数据的变换是否应用Cache的过期,我们需要想数据库中添加Message。我们通过下面的方式现在message的添加。

Code
private void CreateMessageEntry(string userName, string message)
{
Database db = DatabaseFactory.CreateDatabase();
string insertSql = "INSERT INTO [dbo].[Messages]([UserID],[Message])VALUES(@userID, @message)";
DbCommand command = db.GetSqlStringCommand(insertSql);
db.AddInParameter(command, "userID", DbType.String, userName);
db.AddInParameter(command, "message", DbType.String, message);
db.ExecuteNonQuery(command);
}
我们的Add按钮的实现如下:基于我们选择的Username和输入的message的内容向DB中添加Message,然后调用GetMessages()方法获取基于用户Foo的Message列表。之所以要在两者之间将线程休眠1s,是为了上SqlDependency有足够的时间结果从Database传过来的Query Notification,并触发OnChanged事件并执行相应的Event Handler,这样调用GetMessages时检测Cache才能检测到cache item已经过期了。

Code
private void buttonAdd_Click(object sender, EventArgs e)
{
this.CreateMessageEntry(this.comboBoxUserName.SelectedValue.ToString(), this.textBoxMessage.Text.Trim());
Thread.Sleep(1000);
this.listBoxMessage.DataSource = this.GetMessages();
}
由于我们缓存了用户Foo的Message list,所以当我们为Foo创建Message的时候,下面的ListBox的列表能够及时更新,这表明我们的cache item已经过期了。而我们为其他的用户(Bar,Baz)创建Message的时候,cache item将不会过期,这一点我们可以通过弹出的MessageBox探测掉(expiration.Expired += delegate MessageBox.Show("Cache has expired!");}; ),只有前者才会弹出下面的MessageBox:

附件:
您所在的用户组无法下载或查看附件注:由于SqlDependency建立在Service Broker之上的,所以我们必须将service Broker开关打开(默认使关闭的)。否则我们将出现下面的错误:

附件:
您所在的用户组无法下载或查看附件打开service Broker可以通过如下的T-SQL:ALTER DATABASE MyDb SET ENABLE_BROKER ;
Source Code:

附件:
您所在的用户组无法下载或查看附件