当前位置:文档之家› 给DAL层加上Cache(张宁)

给DAL层加上Cache(张宁)

给DAL层加上Cache(张宁)
给DAL层加上Cache(张宁)

给DAL层加上Cache

如何给DAL层加上Cache?通常关系型数据库中的一条记录对应一个实体,for example:User表中的一条记录对应User类的一个实例,就两个字段用户编号(ID)和姓名(Name)。好了,那么在DAL中我们通常有个根据用户编号得到User对象,

方法的通常如下:

Public User getObj(int id);

{

Return GetUserFormDB(id);

}

好了以上就是我们通常的写法,每次从数据库中去。

有时为了性能,减少访问数据库的次数,或者更确切的说是减少IO次数,希望从内存中取数据。现在给这个方法加上Cache如下:

Public User getObj(int id)

{

//代码1

if(IsExistCache(id)) //如果对象在缓存中

{

Return GetCache(id);//则从缓存中取得后返回

}

//代码2

User user = GetUserFormDB(id); //否则从数据库中取

//代码3

if(user!=null)

{

SetCache(user); //如果从数据库中取出的不为空,则加入缓存中。

}

Return user;

}

这个方法比之前的方法多了大概8行代码,所加的代码几乎可以成为一个模式,google一把您就知道。But,这仅仅是一个User类中的一个getObj方法(根据主键值得到对应的记录)。就写了8行,如果是100类个呢?1000个呢?10000个呢?即使你会说,咱们有CodeSmith,咱们是码农,咱们可以加班到凌晨2点。OK,那我再问:即使用CodeSmith可以生成了,那么我们通常在开发调试时都是不需要缓存的,它会干扰我们,我们需要直接访问数据库,而这里的缓存会妨碍我们的调试,但是现在没办法CodeSmith已经帮我们都生成好了,咋办?好,您说我们可以在系统上线之前再改,因为我们在DAL中可以写2个版本的方法(一个叫getobj,另一个叫getobj_Cache),开发时用前者,上线之前可以全局替换所有BLL中的getobj为getobj_Cache。好了,我们现在是在给自己挂一个坑,然后再往里跳。上面方法的问题出在访问数据库和访问Cache这两件事耦合到了一起,我们要把这两件事剥离开,我们只关心从数据库中取,而让跟缓存有关系的事情交给另一个对象去做。就像把上面的方法从中间切开,能实现这个功能的也就是AOP了,什么叫AOP,您google一把就知道,关于AOP的框架很多,在这里用Castle实现一把,大概意思是:按照以前的方式去写(从数据库中取就一行代码),把跟缓存有关的事都剥离出去,让一个对象去处理,这个对象一般叫拦截器,当我们调用getObj(id)方法,在执行其中的GetUserFormDB(id)方法前拦截器先拦截

并且执行代码1,然后执行代码2,接着再一次拦截并执行代码3,大概意思就这样,下面看下代码片段:

首先是一个DAL中的GetObj(id)方法:

[Cache("User",OperateEnum.Get)]

public virtual User GetObj([CacheKey]int Id)

{

using (DbDataReader dr = DbHelper.ExecuteReader(CommandType.Text, "s elect * from Users where id=" + Id))

{

if (dr.Read())

{

User u = new User();

https://www.doczj.com/doc/0214099573.html,erName = dr.GetString(dr.GetOrdinal(“uname”));

return u;

}

return null;

}

}

两个自定义的CustomerAttribute和一个枚举类型:

[AttributeUsage(AttributeTargets.Method,AllowMultiple = false)] public class CacheAttribute:Attribute

{

public string KeyName;

public OperateEnum Operate;

public CacheAttribute(string _key, OperateEnum _enum)

{

KeyName = _key;

OperateEnum = _enum;

}

}

public enum OperateEnum

{

Set,

Get

}

[AttributeUsage(AttributeTargets.Parameter,AllowMultiple = false)] public class CacheKeyAttribute: Attribute{}

说明如下:

1、getobj方法是虚的,因为只有虚方法拦截器才能拦住。

2、CacheAttribute第一个参数是你自己定义的Cache的key,这里我默认写成和

类名一样(其实CodeSimth生成DAL时也可一起生成),其实真正的Cache的key还要加上getobj方法的参数id(这一步会在拦截器中做掉),如:

Cache.add(“User”+id,user);

3、CacheAttribute第二个参数是个枚举,说明我现在这个方法是select,还是

update和Delete,因为拦截器不仅要拦截查询方法,还要拦截修改和删除方法,以便在你执行DelObj(id)、UpdateObj(id)时同步删除Cache中的对象。

4、CacheKeyAttribute这个类啥都没,它只能标记在参数上,它要告诉拦截器我

标记的这个参数就是Cache的key中的id。

好了,下面主角登场了拦截器类:

说明如下:

1、拦截器类要实现IInterceptor接口,并实现Intercept方法。

2、15行得到所有标记了CacheAttribute的方法。

3、21行的循环得到了标记了CacheKey的参数序号。

4、39行得到了真正Cache的key。

5、42-59行就是那部分被剥离出来的与缓存相关事情,现在交给了拦截器去做,

同时第25行才是去从数据库中取,54行如果返回值也就是从数据库中取到的对象不会空,56行则添加入缓存中。

6、61-65行如果您调用的是update和delete方法,则从缓存中删除,65行并

且执行真正的Delete或update方法。

调用时,修改一下DAL对象的实例化方式:

原来我们是直接New一个:

private static void GetProvider()

{

_instance = new UserDAL();

}

现在:

private static void GetProvider()

{

ProxyGenerator generator = new ProxyGenerator();

_instance = generator.CreateClassProxy(new

DALInterceprot());

}

ProxyGenerator是一个Castle提供的一个代理工厂类,用它Create一个DAL 实例。

总结:

1、如果现在就按以上方法写了DAL,如果想在开发时直接访问数据库,而不要

受缓存的干扰,那么就把您方法前的virtual关键字去掉,去掉后拦截器就不会拦截。

2、拦截器类和那两个自定义的Attribute类在任何项目可以重用。

3、在DAL类方法上标记的自定义标签可以用CodeSimth自动生成,即使自己写

一个CodeSimth模板中没有的方法也再简单不过了,比如:

[Cache("City", OperateEnum.Get)]

Public virtual Dictionary GetCityList(){}

4、有了AOP就可以不要在再每个页面的按钮事件中写步骤了,全部放到BLL层

用拦截器去拦截,只需要在放上标记一些Attribute。

5、如果以后我们想用xml缓存,或者用Memcached缓存,那只需要修改拦截器

即可。

相关主题
文本预览
相关文档 最新文档