<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 30, 文章 - 0, 評(píng)論 - 9, 引用 - 0
    數(shù)據(jù)加載中……

    在PetShop 4.0中ASP.NET緩存的實(shí)現(xiàn)

    PetShop作為一個(gè)B2C的寵物網(wǎng)上商店,需要充分考慮訪客的用戶體驗(yàn),如果因?yàn)閿?shù)據(jù)量大而導(dǎo)致Web服務(wù)器的響應(yīng)不及時(shí),頁(yè)面和查詢數(shù)據(jù)遲遲得不到結(jié)果,會(huì)因此而破壞客戶訪問(wèn)網(wǎng)站的心情,在耗盡耐心的等待后,可能會(huì)失去這一部分客戶。無(wú)疑,這是非常糟糕的結(jié)果。因而在對(duì)其進(jìn)行體系架構(gòu)設(shè)計(jì)時(shí),整個(gè)系統(tǒng)的性能就顯得殊為重要。然而,我們不能因噎廢食,因?yàn)閷?zhuān)注于性能而忽略數(shù)據(jù)的正確性。在PetShop 3.0版本以及之前的版本,因?yàn)锳SP.NET緩存的局限性,這一問(wèn)題并沒(méi)有得到很好的解決。PetShop 4.0則引入了SqlCacheDependency特性,使得系統(tǒng)對(duì)緩存的處理較之以前大為改觀。

    4.3.1  CacheDependency接口

    PetShop 4.0引入了SqlCacheDependency特性,對(duì)Category、Product和Item數(shù)據(jù)表對(duì)應(yīng)的緩存實(shí)施了SQL Cache Invalidation技術(shù)。當(dāng)對(duì)應(yīng)的數(shù)據(jù)表數(shù)據(jù)發(fā)生更改后,該技術(shù)能夠?qū)⑾嚓P(guān)項(xiàng)從緩存中移除。實(shí)現(xiàn)這一技術(shù)的核心是SqlCacheDependency類(lèi),它繼承了CacheDependency類(lèi)。然而為了保證整個(gè)架構(gòu)的可擴(kuò)展性,我們也允許設(shè)計(jì)者建立自定義的CacheDependency類(lèi),用以擴(kuò)展緩存依賴。這就有必要為CacheDependency建立抽象接口,并在web.config文件中進(jìn)行配置。

    在PetShop 4.0的命名空間PetShop.ICacheDependency中,定義了名為IPetShopCacheDependency接口,它僅包含了一個(gè)接口方法:
    public interface IPetShopCacheDependency
    {      
        AggregateCacheDependency GetDependency();
    }

    AggregateCacheDependency是.Net Framework 2.0新增的一個(gè)類(lèi),它負(fù)責(zé)監(jiān)視依賴項(xiàng)對(duì)象的集合。當(dāng)這個(gè)集合中的任意一個(gè)依賴項(xiàng)對(duì)象發(fā)生改變時(shí),該依賴項(xiàng)對(duì)象對(duì)應(yīng)的緩存對(duì)象都將被自動(dòng)移除。
    AggregateCacheDependency類(lèi)起到了組合CacheDependency對(duì)象的作用,它可以將多個(gè)CacheDependency對(duì)象甚至于不同類(lèi)型的CacheDependency對(duì)象與緩存項(xiàng)建立關(guān)聯(lián)。由于PetShop需要為Category、Product和Item數(shù)據(jù)表建立依賴項(xiàng),因而IPetShopCacheDependency的接口方法GetDependency()其目的就是返回建立了這些依賴項(xiàng)的AggregateCacheDependency對(duì)象。

    4.3.2  CacheDependency實(shí)現(xiàn)

    CacheDependency的實(shí)現(xiàn)正是為Category、Product和Item數(shù)據(jù)表建立了對(duì)應(yīng)的SqlCacheDependency類(lèi)型的依賴項(xiàng),如代碼所示:
    public abstract class TableDependency : IPetShopCacheDependency
    {
        // This is the separator that's used in web.config
        protected char[] configurationSeparator = new char[] { ',' };

        protected AggregateCacheDependency dependency = new AggregateCacheDependency();
        protected TableDependency(string configKey)
        {
            string dbName = ConfigurationManager.AppSettings["CacheDatabaseName"];
            string tableConfig = ConfigurationManager.AppSettings[configKey];
            string[] tables = tableConfig.Split(configurationSeparator);

            foreach (string tableName in tables)
                dependency.Add(new SqlCacheDependency(dbName, tableName));
        }
        public AggregateCacheDependency GetDependency()
       {
            return dependency;
        }
    }

    需要建立依賴項(xiàng)的數(shù)據(jù)庫(kù)與數(shù)據(jù)表都配置在web.config文件中,其設(shè)置如下:
    <add key="CacheDatabaseName" value="MSPetShop4"/>
    <add key="CategoryTableDependency" value="Category"/>
    <add key="ProductTableDependency" value="Product,Category"/>
    <add key="ItemTableDependency" value="Product,Category,Item"/>

    根據(jù)各個(gè)數(shù)據(jù)表間的依賴關(guān)系,因而不同的數(shù)據(jù)表需要建立的依賴項(xiàng)也是不相同的,從配置文件中的value值可以看出。然而不管建立依賴項(xiàng)的多寡,其創(chuàng)建的行為邏輯都是相似的,因而在設(shè)計(jì)時(shí),抽象了一個(gè)共同的類(lèi)TableDependency,并通過(guò)建立帶參數(shù)的構(gòu)造函數(shù),完成對(duì)依賴項(xiàng)的建立。由于接口方法GetDependency()的實(shí)現(xiàn)中,返回的對(duì)象dependency是在受保護(hù)的構(gòu)造函數(shù)創(chuàng)建的,因此這里的實(shí)現(xiàn)方式也可以看作是Template Method模式的靈活運(yùn)用。例如TableDependency的子類(lèi)Product,就是利用父類(lèi)的構(gòu)造函數(shù)建立了Product、Category數(shù)據(jù)表的SqlCacheDependency依賴:
    public class Product : TableDependency
    {
        public Product() : base("ProductTableDependency") { }
    }

    如果需要自定義CacheDependency,那么創(chuàng)建依賴項(xiàng)的方式又有不同。然而不管是創(chuàng)建SqlCacheDependency對(duì)象,還是自定義的CacheDependency對(duì)象,都是將這些依賴項(xiàng)添加到AggregateCacheDependency類(lèi)中,因而我們也可以為自定義CacheDependency建立專(zhuān)門(mén)的類(lèi),只要實(shí)現(xiàn)IPetShopCacheDependency接口即可。

    4.3.3  CacheDependency工廠

    繼承了抽象類(lèi)TableDependency的Product、Category和Item類(lèi)均需要在調(diào)用時(shí)創(chuàng)建各自的對(duì)象。由于它們的父類(lèi)TableDependency實(shí)現(xiàn)了接口IPetShopCacheDependency,因而它們也間接實(shí)現(xiàn)了IPetShopCacheDependency接口,這為實(shí)現(xiàn)工廠模式提供了前提。

    在PetShop 4.0中,依然利用了配置文件和反射技術(shù)來(lái)實(shí)現(xiàn)工廠模式。命名空間PetShop.CacheDependencyFactory中,類(lèi)DependencyAccess即為創(chuàng)建IPetShopCacheDependency對(duì)象的工廠類(lèi):
    public static class DependencyAccess
    {       
        public static IPetShopCacheDependency CreateCategoryDependency()
        {
            return LoadInstance("Category");
        }
        public static IPetShopCacheDependency CreateProductDependency()
        {
            return LoadInstance("Product");
        }
        public static IPetShopCacheDependency CreateItemDependency()
        {
            return LoadInstance("Item");
        }
        private static IPetShopCacheDependency LoadInstance(string className)
        {
            string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"];
            string fullyQualifiedClass = path + "." + className;
            return (IPetShopCacheDependency)Assembly.Load(path).CreateInstance(fullyQualifiedClass);
        }
    }
    整個(gè)工廠模式的實(shí)現(xiàn)如圖4-3所示:

    4-3.gif
     圖4-3 CacheDependency工廠

    雖然DependencyAccess類(lèi)創(chuàng)建了實(shí)現(xiàn)了IPetShopCacheDependency接口的類(lèi)Category、Product、Item,然而我們之所以引入IPetShopCacheDependency接口,其目的就在于獲得創(chuàng)建了依賴項(xiàng)的AggregateCacheDependency類(lèi)型的對(duì)象。我們可以調(diào)用對(duì)象的接口方法GetDependency(),如下所示:
    AggregateCacheDependency dependency = DependencyAccess.CreateCategoryDependency().GetDependency();

    為了方便調(diào)用者,似乎我們可以對(duì)DependencyAccess類(lèi)進(jìn)行改進(jìn),將原有的CreateCategoryDependency()方法,修改為創(chuàng)建AggregateCacheDependency類(lèi)型對(duì)象的方法。

    然而這樣的做法擾亂了作為工廠類(lèi)的DependencyAccess的本身職責(zé),且創(chuàng)建IPetShopCacheDependency接口對(duì)象的行為仍然有可能被調(diào)用者調(diào)用,所以保留原有的DependencyAccess類(lèi)仍然是有必要的。

    在PetShop 4.0的設(shè)計(jì)中,是通過(guò)引入Facade模式以方便調(diào)用者更加簡(jiǎn)單地獲得AggregateCacheDependency類(lèi)型對(duì)象。

    4.3.4  引入Facade模式

    利用Facade模式可以將一些復(fù)雜的邏輯進(jìn)行包裝,以方便調(diào)用者對(duì)這些復(fù)雜邏輯的調(diào)用。就好像提供一個(gè)統(tǒng)一的門(mén)面一般,將內(nèi)部的子系統(tǒng)封裝起來(lái),統(tǒng)一為一個(gè)高層次的接口。一個(gè)典型的Facade模式示意圖如下所示:

    4-4.gif
    圖4-4 Facade模式

    Facade模式的目的并非要引入一個(gè)新的功能,而是在現(xiàn)有功能的基礎(chǔ)上提供一個(gè)更高層次的抽象,使得調(diào)用者可以直接調(diào)用,而不用關(guān)心內(nèi)部的實(shí)現(xiàn)方式。以CacheDependency工廠為例,我們需要為調(diào)用者提供獲得AggregateCacheDependency對(duì)象的簡(jiǎn)便方法,因而創(chuàng)建了DependencyFacade類(lèi):
    public static class DependencyFacade
    {
        private static readonly string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"];
        public static AggregateCacheDependency GetCategoryDependency()
        {
            if (!string.IsNullOrEmpty(path))
                return DependencyAccess.CreateCategoryDependency().GetDependency();
            else
                return null;
        }
        public static AggregateCacheDependency GetProductDependency()
        {
            if (!string.IsNullOrEmpty(path))
                return DependencyAccess.CreateProductDependency().GetDependency();
            else
                return null;
            }
        public static AggregateCacheDependency GetItemDependency()
        {
            if (!string.IsNullOrEmpty(path))
                return DependencyAccess.CreateItemDependency().GetDependency();
            else
                return null;
        }
    }

    DependencyFacade類(lèi)封裝了獲取AggregateCacheDependency類(lèi)型對(duì)象的邏輯,如此一來(lái),調(diào)用者可以調(diào)用相關(guān)方法獲得創(chuàng)建相關(guān)依賴項(xiàng)的AggregateCacheDependency類(lèi)型對(duì)象:
    AggregateCacheDependency dependency = DependencyFacade.GetCategoryDependency();

    比起直接調(diào)用DependencyAccess類(lèi)的GetDependency()方法而言,除了方法更簡(jiǎn)單之外,同時(shí)它還對(duì)CacheDependencyAssembly配置節(jié)進(jìn)行了判斷,如果其值為空,則返回null對(duì)象。

    在PetShop.Web的App_Code文件夾下,靜態(tài)類(lèi)WebUtility的GetCategoryName()和GetProductName()方法調(diào)用了DependencyFacade類(lèi)。例如GetCategoryName()方法:
    public static string GetCategoryName(string categoryId)
    {
         Category category = new Category();
         if (!enableCaching)
                return category.GetCategory(categoryId).Name;

         string cacheKey = string.Format(CATEGORY_NAME_KEY, categoryId);

         // 檢查緩存中是否存在該數(shù)據(jù)項(xiàng);
         string data = (string)HttpRuntime.Cache[cacheKey];
         if (data == null)
         {
               // 通過(guò)web.config的配置獲取duration值;
               int cacheDuration = int.Parse(ConfigurationManager.AppSettings["CategoryCacheDuration"]);
               // 如果緩存中不存在該數(shù)據(jù)項(xiàng),則通過(guò)業(yè)務(wù)邏輯層訪問(wèn)數(shù)據(jù)庫(kù)獲取;
               data = category.GetCategory(categoryId).Name;
               // 通過(guò)Facade類(lèi)創(chuàng)建AggregateCacheDependency對(duì)象;
               AggregateCacheDependency cd = DependencyFacade.GetCategoryDependency();
               // 將數(shù)據(jù)項(xiàng)以及AggregateCacheDependency 對(duì)象存儲(chǔ)到緩存中;
               HttpRuntime.Cache.Add(cacheKey, data, cd, DateTime.Now.AddHours(cacheDuration), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
          }
          return data;
    }

    GetCategoryName()方法首先會(huì)檢查緩存中是否已經(jīng)存在CategoryName數(shù)據(jù)項(xiàng),如果已經(jīng)存在,就通過(guò)緩存直接獲取數(shù)據(jù);否則將通過(guò)業(yè)務(wù)邏輯層調(diào)用數(shù)據(jù)訪問(wèn)層訪問(wèn)數(shù)據(jù)庫(kù)獲得CategoryName,在獲得了CategoryName后,會(huì)將新獲取的數(shù)據(jù)連同DependencyFacade類(lèi)創(chuàng)建的AggregateCacheDependency對(duì)象添加到緩存中。

    WebUtility靜態(tài)類(lèi)被表示層的許多頁(yè)面所調(diào)用,例如Product頁(yè)面:
    public partial class Products : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Page.Title = WebUtility.GetCategoryName(Request.QueryString["categoryId"]);
        }
    }

    顯示頁(yè)面title的邏輯是放在Page_Load事件方法中,因而每次打開(kāi)該頁(yè)面都要執(zhí)行獲取CategoryName的方法。如果沒(méi)有采用緩存機(jī)制,當(dāng)Category數(shù)據(jù)較多時(shí),頁(yè)面的顯示就會(huì)非常緩慢。

    4.3.5  引入Proxy模式

    業(yè)務(wù)邏輯層BLL中與Product、Category、Item有關(guān)的業(yè)務(wù)方法,其實(shí)現(xiàn)邏輯是調(diào)用數(shù)據(jù)訪問(wèn)層(DAL)對(duì)象訪問(wèn)數(shù)據(jù)庫(kù),以獲取相關(guān)數(shù)據(jù)。為了改善系統(tǒng)性能,我們就需要為這些實(shí)現(xiàn)方法增加緩存機(jī)制的邏輯。當(dāng)我們操作增加了緩存機(jī)制的業(yè)務(wù)對(duì)象時(shí),對(duì)于調(diào)用者而言,應(yīng)與BLL業(yè)務(wù)對(duì)象的調(diào)用保持一致。也即是說(shuō),我們需要引入一個(gè)新的對(duì)象去控制原來(lái)的BLL業(yè)務(wù)對(duì)象,這個(gè)新的對(duì)象就是Proxy模式中的代理對(duì)象。

    以PetShop.BLL.Product業(yè)務(wù)對(duì)象為例,PetShop為其建立了代理對(duì)象ProductDataProxy,并在GetProductByCategory()等方法中,引入了緩存機(jī)制,例如:
    public static class ProductDataProxy
    {

        private static readonly int productTimeout = int.Parse(ConfigurationManager.AppSettings["ProductCacheDuration"]);
        private static readonly bool enableCaching = bool.Parse(ConfigurationManager.AppSettings["EnableCaching"]);
           
        public static IList
    GetProductsByCategory(string category)
        {
            Product product = new Product();

            if (!enableCaching)
                return product.GetProductsByCategory(category);

            string key = "product_by_category_" + category;
            IList data = (IList )HttpRuntime.Cache[key];

            // Check if the data exists in the data cache
            if (data == null)
            {
                data = product.GetProductsByCategory(category);

                // Create a AggregateCacheDependency object from the factory
                AggregateCacheDependency cd = DependencyFacade.GetProductDependency();

                // Store the output in the data cache, and Add the necessary AggregateCacheDependency object
                HttpRuntime.Cache.Add(key, data, cd, DateTime.Now.AddHours(productTimeout), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
            }
            return data;
        }
    }

    與業(yè)務(wù)邏輯層Product對(duì)象的GetProductsByCategory()方法相比,增加了緩存機(jī)制。當(dāng)緩存內(nèi)不存在相關(guān)數(shù)據(jù)項(xiàng)時(shí),則直接調(diào)用業(yè)務(wù)邏輯層Product的GetProductsByCategory()方法來(lái)獲取數(shù)據(jù),并將其與對(duì)應(yīng)的AggregateCacheDependency對(duì)象一起存儲(chǔ)在緩存中。

    引入Proxy模式,實(shí)現(xiàn)了在緩存級(jí)別上對(duì)業(yè)務(wù)對(duì)象的封裝,增強(qiáng)了對(duì)業(yè)務(wù)對(duì)象的控制。由于暴露在對(duì)象外的方法是一致的,因而對(duì)于調(diào)用方而言,調(diào)用代理對(duì)象與真實(shí)對(duì)象并沒(méi)有實(shí)質(zhì)的區(qū)別。

    從職責(zé)分離與分層設(shè)計(jì)的角度分析,我更希望這些Proxy對(duì)象是被定義在業(yè)務(wù)邏輯層中,而不像在PetShop的設(shè)計(jì)那樣,被劃分到表示層UI中。此外,如果需要考慮程序的可擴(kuò)展性與可替換性,我們還可以為真實(shí)對(duì)象與代理對(duì)象建立統(tǒng)一的接口或抽象類(lèi)。然而,單以PetShop的表示層調(diào)用來(lái)看,采用靜態(tài)類(lèi)與靜態(tài)方法的方式,或許更為合理。我們需要謹(jǐn)記,“過(guò)度設(shè)計(jì)”是軟件設(shè)計(jì)的警戒線。

    如果需要對(duì)UI層采用緩存機(jī)制,將應(yīng)用程序數(shù)據(jù)存放到緩存中,就可以調(diào)用這些代理對(duì)象。以ProductsControl用戶控件為例,調(diào)用方式如下:
    productsList.DataSource = ProductDataProxy.GetProductsByCategory(categoryKey);

    productsList對(duì)象屬于自定義的CustomList類(lèi)型,這是一個(gè)派生自System.Web.UI.WebControls.DataList控件的類(lèi),它的DataSource屬性可以接受IList集合對(duì)象。
    不過(guò)在PetShop 4.0的設(shè)計(jì)中,對(duì)于類(lèi)似于ProductsControl類(lèi)型的控件而言,采用的緩存機(jī)制是頁(yè)輸出緩存。我們可以從ProductsControl.ascx頁(yè)面的Source代碼中發(fā)現(xiàn)端倪:
    <%@ OutputCache Duration="100000" VaryByParam="page;categoryId" %>

    與ASP.NET 1.x的頁(yè)輸出緩存不同的是,在ASP.NET 2.0中,為ASP.NET用戶控件新引入了CachePolicy屬性,該屬性的類(lèi)型為ControlCachePolicy類(lèi),它以編程方式實(shí)現(xiàn)了對(duì)ASP.NET用戶控件的輸出緩存設(shè)置。我們可以通過(guò)設(shè)置ControlCachePolicy類(lèi)的Dependency屬性,來(lái)設(shè)置與該用戶控件相關(guān)的依賴項(xiàng),例如在ProductsControl用戶控件中,進(jìn)行如下的設(shè)置:
    protected void Page_Load(object sender, EventArgs e)
    {
        this.CachePolicy.Dependency = DependencyFacade.GetProductDependency();
    }

    采用頁(yè)輸出緩存,并且利用ControlCachePolicy設(shè)置輸出緩存,能夠?qū)I(yè)務(wù)數(shù)據(jù)與整個(gè)頁(yè)面放入到緩存中。這種方式比起應(yīng)用程序緩存而言,在性能上有很大的提高。同時(shí),它又通過(guò)引入的SqlCacheDependency特性有效地避免了“數(shù)據(jù)過(guò)期”的缺點(diǎn),因而在PetShop 4.0中被廣泛采用。相反,之前為Product、Category、Item業(yè)務(wù)對(duì)象建立的代理對(duì)象則被“投閑散置”,僅僅作為一種設(shè)計(jì)方法的展示而“幸存”與整個(gè)系統(tǒng)的源代碼中。

    posted on 2007-12-26 12:07 風(fēng)雨兼程 閱讀(1276) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): Petshop4.0 案例分析

    評(píng)論

    # re: 在PetShop 4.0中ASP.NET緩存的實(shí)現(xiàn)   回復(fù)  更多評(píng)論   

    寫(xiě)的好!!!

    http://www.langligelang.com 啷哩咯啷
    2008-10-31 22:57 | mydip
    主站蜘蛛池模板: 亚洲一区欧洲一区| 国产成人99久久亚洲综合精品| 亚洲AV本道一区二区三区四区| 亚洲免费无码在线| 日韩中文无码有码免费视频 | 国产亚洲精久久久久久无码AV| 亚洲AV成人一区二区三区观看 | 97在线观免费视频观看| 亚洲一区二区三区免费视频| 麻豆最新国产剧情AV原创免费 | 久久嫩草影院免费看夜色| 亚洲人成无码www久久久| 国产在线观看无码免费视频| 亚洲精品无码av人在线观看| 久久伊人免费视频| 亚洲美女激情视频| 成人毛片18女人毛片免费| 天天综合亚洲色在线精品| 亚洲免费观看视频| 暖暖免费在线中文日本| 亚洲国产中文在线视频| 色www永久免费视频| 亚洲av乱码一区二区三区按摩| 亚洲AV无码成H人在线观看| 成年女人A毛片免费视频| 亚洲天堂视频在线观看| 久久久www成人免费毛片| 国产成人久久精品亚洲小说| 亚洲乱码中文字幕久久孕妇黑人| 鲁丝片一区二区三区免费| 亚洲一本之道高清乱码| xvideos亚洲永久网址| 国产免费拔擦拔擦8X高清在线人 | 国产一区二区免费在线| 中国videos性高清免费| 午夜在线a亚洲v天堂网2019| 免费中文字幕在线| 99久久综合精品免费| 高h视频在线免费观看| 久久精品亚洲精品国产色婷| 国产无遮挡吃胸膜奶免费看 |