2011年1月28日 星期五

MVC3 Partial Output Cache - VaryByParam Demo

在本Blog前一篇文章中(MVC3 Partial Output Cache - Simple Demo),我們舉了一個簡單的例子,來說明MVC 3中的Partial Output Cache機制,那個例子在同一個網頁上顯示兩個時間字串,一個時間字串沒有cache,另一個時間字串有設定cache 10秒鐘,由那個例子可知道MVC 3 Partial Output Cache運作的基本方式與程式寫法。


但一般網站中都是佈滿各種資訊在一個web page中,有些需顯示即時資訊,有些靜態資訊則會設定cache,以加快網頁顯示速度,本篇文章中,我們以Northwind資料庫為例,針對查詢同一個產品類別(CategoryID)的結果,來進行Partial Output Cache:也就是透過設定VaryByParam的方式(本篇範例為VaryByParam=CategoryID),只要有網友查詢過A產品類別(CategoryID=A)之下所屬的所有產品,則查詢後的內容就會被cache起來,在設定的cache時間內(如Duration=100),若有網友又要查詢A這個產品類別(CategoryID=A),則伺服器不會再執行查詢程式,而是將cache中之前查過的相同內容直接輸出給第二次查詢同產品類別的網友,當然輸出部分,我們也是以之前文章所提到的Partial View()的方式來製作。我們以下先進行本範例展示,文章後半部再詳述各部份程式。


Demo


[02:17:11 PM] 點選/Product/QueryProduct這頁,可看到我們可下拉產品類別(CategoryID)來選擇我們要查詢的產品,如下圖:



[02:17:19 PM] 選擇CategoryID = Meat/Poultry,可看到目前該類別共有6個產品。如下圖:



[02:17:37 PM] 點選/Product/Create這頁,我們現在新增一個ProductName = KaKa,其CategoryID = Meat/Poultry,就是上個步驟所看到的那個產品類別。如下圖:



[02:17:49 PM] 再次回到/Product/QueryProduct這頁,如下圖選擇CategoryID = Meat/Poultry,可看到目前該類別仍維持有6個產品,伺服器並沒有再次執行查詢的動作,而是將Cache的內容直接回應(response)給查詢者,Cache機制產生效果了(OutputCache is working),而且又上方的時間仍然持續的被更新為最新時間(time is updated continually),所以Partial Output Cache 也產生效果了(Partial OutputCache is also working)。



[02:18:56 PM] 使用另外一個Browser,如下圖點選/Product/QueryProduct這頁,可看到目前該類別仍維持有6個產品,所以Cache機制持續生效中。



[02:19:10 PM] 時間過了我們設定的Cache時間(Duration=100秒),再次更新(refresh)一次/Product/QueryProduct這頁,可看到目前該類別已經變成7個產品,前面步驟新增的ProductName = KaKa這筆資料,現在已經被顯示出來,伺服器已經再次去執行查詢,並取回資料庫最新資料狀況,然後更新Cache內容。



下面我們詳述各部份程式如何撰寫。


Create a MVC3 Project


本範例中先建立一個MVC3的專案檔,並且配合Entity Framework Code First CTP5來連結與操作Northwind資料庫。建立好專案檔,加入EntityFramework.dll參考(Add reference),如下圖:



並加入資料庫連線到Web.config後(請參考之前文章這裡),請先建立Product這個Controller,並且ProductController之下,可去Create一個新Product的View與相關程式,這部份請參考一般MVC的作法,可參考Adding a Create Method and Create View


Model


這部分我們撰寫兩個cs檔案,用來處理與Category與Products相關的資料存取。
而QueryProductViewModel.cs則是上述Demo網頁(/Product/QueryProduct這頁)所用到的View Model描述。


1./Models/CategoryRepository.cs


   1:  public class CategoryRepository
   2:  {
   3:      Northwind northwind;
   4:      public CategoryRepository()
   5:      {
   6:          northwind = new Northwind();
   7:      }
   8:   
   9:      public IQueryable<Category> SelectAll()
  10:      {
  11:          return northwind.Categories;
  12:      }
  13:  }

2./Models/ProductRepository.cs


   1:  public class ProductRepository
   2:  {
   3:      Northwind northwind;
   4:      public ProductRepository()
   5:      {
   6:          northwind = new Northwind();
   7:      }
   8:   
   9:      public IEnumerable<Product> SelectAll()
  10:      {
  11:          return northwind.Products;
  12:      }
  13:   
  14:      public void Create(Product product)
  15:      {
  16:          northwind.Products.Add(product);
  17:          northwind.SaveChanges();
  18:      }
  19:   
  20:      public IQueryable<Product> Select(int id)
  21:      {
  22:          var list = from m in northwind.Products
  23:                     where m.CategoryID == id
  24:                     select m;
  25:          return list;
  26:      }
  27:  }

3./ViewModels/QueryProductViewModel.cs


   1:  public class QueryProductViewModel
   2:  {
   3:      public int CategoryID { get; set; }
   4:      public IQueryable<Product>  Products { get; set; }
   5:  }


Controller


這部分是本範例最重要的部份,我們在Product這個Controller中撰寫QueryProductPartialView這個Action,並且設定相關的OutputCache參數,這個Action,可以用來產生產生Partial Output Cache的Partial View。


1./Controllers/ProductController.cs


   1:  public class ProductController : Controller
   2:  {
   3:      ProductRepository productRep;
   4:      CategoryRepository categoryRep;
   5:      public ProductController()
   6:      {
   7:          productRep = new ProductRepository();
   8:          categoryRep = new CategoryRepository();
   9:      }
  10:   
  11:      public ActionResult QueryProduct()
  12:      {
  13:          ViewBag.Categories = categoryRep.SelectAll();
  14:          QueryProductViewModel qryPrd=new QueryProductViewModel();
  15:          return View(qryPrd);
  16:      }
  17:   
  18:      [HttpPost]
  19:      public ActionResult QueryProduct(QueryProductViewModel QryPrdView)
  20:      {
  21:          ViewBag.Categories = categoryRep.SelectAll();
  22:          QueryProductViewModel prds = new QueryProductViewModel()
  23:          {
  24:              CategoryID = QryPrdView.CategoryID
  25:          };
  26:          return View(prds);
  27:      }
  28:   
  29:      [OutputCache(Duration = 100, VaryByParam = "CategoryID")]
  30:      public ActionResult QueryProductPartialView(int CategoryID)
  31:      {
  32:          var result = productRep.Select(CategoryID);
  33:          QueryProductViewModel prds = new QueryProductViewModel()
  34:          {
  35:              CategoryID = CategoryID,
  36:              Products = result
  37:          };
  38:          return PartialView(prds);
  39:      }
  40:      
  41:      public ActionResult Create()
  42:      {
  43:      //請參考http://www.asp.net/mvc
  44:      } 
  45:   
  46:      [HttpPost]
  47:      public ActionResult Create(Product collection)
  48:      {
  49:      //請參考http://www.asp.net/mvc
  50:      }    
  51:  }


View


在查詢網頁上我們會產生一個可選擇不同產品類別(CategoryID)的DropDownList,然後下方會即時顯示查詢這屬於這類別的所有產品清單,顯示產品清單這部份,我們是用Partial View的方式來處理,所以我們也需撰寫QueryProductPartialView.cshtml,如下所示:


1./Views/Product/QueryProduct.cshtml


   1:  @model Mvc3Demo.ViewModels.QueryProductViewModel
   2:  <h2>QueryProduct</h2>
   3:   
   4:  @using (Html.BeginForm())
   5:  {
   6:      <div class="editor-field">Category Name:
   7:          @Html.DropDownListFor(model => model.CategoryID,
   8:              new SelectList(ViewBag.Categories,
   9:                  "CategoryID", "CategoryName"),
  10:              new { onchange = "this.form.submit();" })
  11:      </div>
  12:  }
  13:  @Html.Action("QueryProductPartialView", "Product",
  14:      new { CategoryID = Model.CategoryID })

2./Views/Product/QueryProductPartialView.cshtml


   1:  @model Mvc3Demo.ViewModels.QueryProductViewModel
   2:   
   3:  @if (Model.Products != null)
   4:  {
   5:  if (Model.Products.Count() > 0)
   6:  { 
   7:  <table>
   8:      <tr>
   9:          <th>Total:@Model.Products.Count()</th>
  10:          <th>
  11:              CategoryID
  12:          </th>
  13:          <th>
  14:              ProductName
  15:          </th>
  16:          <th>
  17:              UnitPrice
  18:          </th>
  19:      </tr>
  20:   
  21:  @foreach (var item in Model.Products)
  22:  {
  23:      <tr>
  24:          <td>
  25:              @Html.ActionLink("Edit", "Edit",
  26:                   new { id = item.ProductID })
  27:          </td>
  28:          <td>
  29:              @item.CategoryID
  30:          </td>
  31:          <td>
  32:              @item.ProductName
  33:          </td>
  34:          <td>
  35:              @String.Format("{0:F}", item.UnitPrice)
  36:          </td>
  37:      </tr>
  38:  }
  39:   
  40:  </table>
  41:  }
  42:  }

以上即是本範例的程式碼,Output Cache可幫助提供更快速的網頁瀏覽經驗,Partial Output Cache 更可彈性的提供部分內容的Cache機制。

沒有留言:

張貼留言