給DAL層加上Cache

Public User getObj(int id);
{
                            Return GetUserFormDB(id);
}
給DAL層加上Cache
        如何給DAL層加上Cache?通常關係型數據庫中的一條記錄對應一個實體,for example:User表中的一條記錄對應User類的一個實例,就兩個字段用戶編號(ID)和姓名(Name)。好了,那麼在DAL中我們通常有個根據用戶編號得到User對象,
方法的通常如下:  
 
好了以上就是我們通常的寫法,每次從數據庫中去。
有時爲了性能,減少訪問數據庫的次數,或者更確切的說是減少IO次數,希望從內存中取數據。現在給這個方法加上Cache如下:
Public User getObj(int id)
{
    //代碼1
    if(IsExistCach(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, "select * from Users where id=" + Id))
     {
                if (dr.Read())
                {
                         User u = new User();
                                            u.UserName = 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<UserDAL>(new DALInterceprot());
}
 
ProxyGenerator是一個Castle提供的一個代理工廠類,用它Create一個DAL實例。
 
總結:
1、 如果現在就按以上方法寫了DAL,如果想在開發時直接訪問數據庫,而不要受緩存的干擾,那麼就把您方法前的virtual關鍵字去掉,去掉後攔截器就不會攔截。
2、 攔截器類和那兩個自定義的Attribute類在任何項目可以重用。
3、 DAL類方法上標記的自定義標籤可以用CodeSimth自動生成,即使自己寫一個CodeSimth模板中沒有的方法也再簡單不過了,比如:
[Cache("City", OperateEnum.Get)]
Public virtual Dictionary<int,string> GetCityList(){}
4、 有了AOP就可以不要在再每個頁面的按鈕事件中寫步驟了,全部放到BLL層用攔截器去攔截,只需要在放上標記一些Attribute。
5、 如果以後我們想用xml緩存,或者用Memcached緩存,那只需要修改攔截器即可。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章