1 写在前面
此文主要参考了园子里以下两篇文章:
黄聪,
顾磊,
2 前面两篇博文写的很好,很全面,为何还需要本文?
大家可以点进去看下前面的文章,黄聪写的是企业库Cache的基本用法,顾磊的文章比较深入,而且自定义了CacheHelper类,实用性更强,我也抄袭了这个类(^_^ )。
我写此文的目的主要是记录下如果在项目中引入操作Cache、缓存哪些内容及最后的单元测试等~
主要是在缓存哪些数据的问题上,可能和顾磊的文章有些不同。
3 项目中引入Cache
首先从微软网站下载并安装Enterprise Library 5.0, 我这里Cache主要用在DataAccess这个项目中,是一个类库项目,所以Config的东西先不用配置,直接添加Microsoft.Practices.EnterpriseLibrary.Caching.dll的引用,
然后添加CacheHelper类,代码:
View Code 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;
using System.Reflection;
namespace DataAccess
{
public static class CacheHelper
{
private static ICacheManager cache = CacheFactory.GetCacheManager();
/// <summary> /// Add Cache /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="isRefresh"> if true, should reload data every 5 mins; default value = false </param> public static void Add(
string key,
object value,
bool isRefresh)
{
if (isRefresh)
cache.Add(key, value, CacheItemPriority.Normal,
new ATSCacheItemRefreshAction(),
new AbsoluteTime(TimeSpan.FromMinutes(
5)));
else cache.Add(key, value);
}
// Summary: // Adds new CacheItem to cache. If another item already exists with the same // key, that item is removed before the new item is added. If any failure occurs // during this process, the cache will not contain the item being added. Items // added with this method will be not expire, and will have a Normal Microsoft.Practices.EnterpriseLibrary.Caching.CacheItemPriority // priority. // // Parameters: // key: // Identifier for this CacheItem // // value: // Value to be stored in cache. May be null. // // Exceptions: // System.ArgumentNullException: // Provided key is null // // System.ArgumentException: // Provided key is an empty string // // Remarks: // The CacheManager can be configured to use different storage mechanisms in // which to store the CacheItems. Each of these storage mechanisms can throw // exceptions particular to their own implementations. public static void Add(
string key,
object value)
{
Add(key, value,
false);
}
public static object Get(
string key)
{
return cache.GetData(key);
}
public static void Remove(
string key)
{
cache.Remove(key);
}
}
[Serializable]
public class ATSCacheItemRefreshAction : ICacheItemRefreshAction
{
#region ICacheItemRefreshAction Members
public void Refresh(
string removedKey,
object expiredValue, CacheItemRemovedReason removalReason)
{
// when expired, reload it. if (removalReason == CacheItemRemovedReason.Expired)
{
ICacheManager c = CacheFactory.GetCacheManager();
c.Add(removedKey, expiredValue);
}
}
#endregion }
4 缓存数据
在顾磊的文章中,他主要缓存一些Class,如ClassInfoService、StudentService等,代码如下:
View Code /// <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 IStudentService CreateStudent()
{
string typeName = assemblyString +
" .StudentService ";
if (CacheHelper.GetCache(typeName) !=
null)
{
return (IStudentService)CacheHelper.GetCache(typeName);
}
else {
IStudentService service = (IStudentService)Assembly.Load(assemblyString).CreateInstance(typeName,
true);
CacheHelper.Add(typeName, service,
true);
return service;
}
而像StudentService这种Class,如果New StudentService()这样一个实例的话,所占的CPU和内存都是很小的,我觉得更有必要的是缓存数据,从数据库中查询回来的数据。
我们看下顾磊代码中如何操作数据的:
class StudentManage:
View Code private static readonly IStudentService studentService = DataAccess<IStudentService>.CreateObject(
" StudentService ");
public Student SelectById(
int id)
{
return studentService.SelectById(id);
class StudentService:
View Code /// <summary> /// 根据学生ID查询学生对象 /// </summary> /// <param name="id"> 学生ID </param> /// <returns></returns> public Student SelectById(
int id)
{
Student student =
null;
Database db = DBHelper.CreateDataBase();
StringBuilder sb =
new StringBuilder();
sb.Append(
" select * from Student ");
sb.Append(
" where ID=@ID ");
DbCommand cmd = db.GetSqlStringCommand(sb.ToString());
db.AddInParameter(cmd,
" @ID ", DbType.Int32, id);
using (IDataReader reader = db.ExecuteReader(cmd))
{
if (reader.Read())
{
student =
new Student()
{
Id = reader.GetInt32(
0),
ClassId = reader.GetInt32(
1),
Sid = reader.GetString(
2),
Password = reader.GetString(
3),
Name = reader.GetString(
4),
Sex = reader.GetInt32(
5),
Birthday = reader.GetDateTime(
6),
IsAdmin = reader.GetInt32(
7)
};
}
}
return student;
大家从上面的代码可以看出,缓存中存放了StudentService这个类,但是在SelectById(int id)这个函数中,并没有缓存任何东西,还是每次从数据库中查询数据,这样设计缓存,对程序性能的提升是十分有限的。
5 我程序中如何缓存数据
我还是用上面顾磊的代码吧,那个好理解。然后我将每次查询到的数据缓存起来,如果下次查询,先从缓存中取数据,有则自动返回数据;没有,则从数据库中查询,然后添加到缓存中,缓存有自动的数据过期机制,过期的数据会自动删除。
View Code public Student SelectByIdWithCache(
int id)
{
Student student = (Student)CacheHelper.GetCache(id.ToString());
if (student ==
null)
{
student = SelectById(id);
CacheHelper.Add(id.ToString(), student);
}
return student;
这里可能上面的代码不是十分恰当,但是为了让大家更清楚我的意图,及上面的代码十分简洁、易懂。
主要是我项目程序中有这样的特情况,查询参数是一个TableParameters,里面有很多字段:
View Code public class TableParameters : IParameters<tblTable>
{
public Guid? tableGuid {
get;
set; }
public string tableName {
get;
set; }
public string tableDesc {
get;
set; }
public Guid? tableTypeGuid {
get;
set; }
public Guid? schemeGuid {
get;
set; }
public Guid index1TypeGuid {
get;
set; }
public Guid? latestTableVersionGuid {
get;
set; }
public Guid? tableFormatGuid {
get;
set; }
public Guid? index2TypeGuid {
get;
set; }
在第一次会通过一个条件,查询得到一个List,之后会直接传输一个Guid过来,而这个guid往往会包含在上面的结果中,所以第一件会将List缓存,详细代码:
View Code public List<tblTable> GetQueryList(IParameters<tblTable> t)
{
TableParameters param = (TableParameters)t;
List<tblTable> query =
new List<tblTable>();
if (param.tableGuid !=
null)
{
tblTable result = (tblTable)CacheHelper.Get(GuidTable + param.tableGuid.Value.ToString());
if (result !=
null)
{
query.Add(result);
return query;
}
}
var _tableQuery =
from c
in db.tblTable.Expand(
" TableType ").Expand(
" TableFormat ").Expand(
" Scheme ").Expand(
" Index1Type ").Expand(
" Index2Type ").Expand(
" LatestTableVersionGUID ")
select c;
if (param.tableGuid !=
null)
{
_tableQuery = _tableQuery.Where(n => n.TableGUID == param.tableGuid.Value);
}
if (!
string.IsNullOrEmpty(param.tableName))
{
_tableQuery = _tableQuery.Where(n => n.TableName.Contains(param.tableName));
}
if (param.tableTypeGuid !=
null)
{
_tableQuery = _tableQuery.Where(n => n.TableType.TableTypeGUID == param.tableTypeGuid.Value);
}
if (param.tableFormatGuid !=
null)
{
_tableQuery = _tableQuery.Where(n => n.TableFormat.TableFormatGUID == param.tableFormatGuid.Value);
}
if (param.schemeGuid !=
null)
_tableQuery = _tableQuery.Where(n => n.Scheme.SchemeGUID == param.schemeGuid.Value);
query = _tableQuery.ToList<tblTable>();
foreach (
var tb
in query)
{
CacheHelper.Add(GuidTable + tb.TableGUID.ToString(), tb);
}
return query;
6 单元测试
这个单元测试不是测试顾磊代码的,是我项目中测试TableManager的,因为顾磊的代码更简明,所以上面我还是贴出了他的代码,至于单元测试,还是随便贴个我项目的,因为顾磊那个我没数据,没法写,呵呵~
View Code /// <summary> /// A test for GetQueryList /// </summary> [TestMethod()]
public void GetQueryListTest()
{
TableParameters t =
new TableParameters();
t.schemeGuid =
new Guid(
" 9C962C55-A598-40B8-A39B-11788161A9D8 ");
List<tblTable> actual;
actual = ManagerFactory.TableManager.GetQueryList(t);
Assert.AreEqual(
" Sedgwick Marsh- B & pre2000-NRA65 ", actual[
0].TableName);
// Assert.Inconclusive("Verify the correctness of this test method."); 7 总结
我认为缓存数据不仅仅要缓存Class类,更要缓存从数据库中查询过来的数据,这样才能最大限度的提示程序性能。
8 声明
以上纯为技术交流,对顾磊、黄聪等技术牛人十分敬佩,文中也引用了他们的文中和大量代码,再次感谢。