Bài viết này là chương XIV của Nhập môn ASP.NET 4.X. Mục tiêu của chương này bao gồm:

  • Định dạng các điều khiển
  • Xử lý các sự kiện
  • Làm việc với Caching

Bài viết sử dụng VS 2017 Community và ngôn ngữ C#, bạn có thể thực hiện các bước thực hành tương tự trong VS 2012 hay VS 2013. Nếu bạn chưa từng làm việc với 13 chương ASP.NET trước đó, có thể truy cập: https://ngocminhtran.com/asp-net-4-5/

Định dạng các điều khiển

Đa phần các điều khiển trong ASP.NET 4.5 (trở lên) được cung cấp các thuộc tính CSS cho phép định dạng bản thân các điều trông thật đẹp mắt và thân thiện với người dùng. Ví dụ, điều khiển GridView có hai thuộc tính là RowStyle và AlternatingRowStyle cho phép định dạng các hàng như đoạn mã sau:


<asp:GridView ID="GridView1" runat="server">

  <AlternatingRowStyle BackColor="White" />

  <RowStyle BackColor="#EFF3FB" />

</asp:GridView>

Đoạn mã trên định dạng các hàng chẵn và lẻ với các màu sắc khác nhau trong GridView.

Mỗi thuộc tính này được thừa kế từ lớp Style trong namespace System.Web.UI.WebControls. Các thông tin liên quan đến định dạng của điều khiển sẽ được chuyển thành các thuộc tính CSS hay các thuộc tính HTML khi trang hiển thị trên trình duyệt web. Bảng sau đây cung cấp một vài thuộc tính hay sử dụng nhất:

Thuộc tính Mô tả
BackColor

ForeColor

Tương ứng với các thuộc tính CSS background-color và color, dùng để thay đổi màu nền (BackColor) và màu chữ (ForeColor) của điều khiển.
BorderColor

BorderStyle

BorderWidth

Liên quan đến thay đổi màu (BorderColor), kiểu (BorderStyle) và độ dày (BorderWidth) của khung viền. Tương ứng với các thuộc tính CSS border-color, border-styleborder-width.
CssClass Cho phép gán lớp CSS thay vì khai báo CSS kiểu inline.
Font Cho phép thiết lập kiểu chữ thông qua các thuộc tính con như Font-Names, Font-Size và Font-Bold. Các thuộc tính này lần lượt tương ứng với các thuộc tính CSS font-family, font-size font-weight.
HorizontalAlign

VerticalAlign

Cho phép căn lề các điều khiển theo chiều ngang như trái, giữa hay phải (HorizontalAlign) và theo chiều dọc như trên, giữa hay dưới (VerticalAlign). Tương đương với các thuộc tính HTML align (chiều ngang) và valign (chiều dọc).
Wrap Cho phép một mẫu văn bản ở chế độ Wrap (bố trí thành nhiều dòng để khớp với không gian chứa văn bản).
Height

Width

Cho phép kiểm soát chiều cao và rộng của một điều khiển.

Các thuộc tính thừa kế từ lớp Style và tên kết thúc bằng từ Style như RowStyle của GridView thường định dạng kiểu (style) cho các điều khiển. Các điều khiển kết buộc dữ liệu (data-bound controls) khác nhau sẽ có tập các thuộc tính định dạng kiểu (style) khác nhau. Một vài điều khiển như Repeater và ListView không chứa các thuộc tính định dạng kiểu vì chúng không cung cấp nội dung HTML đến trang.

Bài thực hành sau đây sẽ giúp chúng ta làm quen với các thuộc tính kiểu trong điều khiển GridView.

Thực hành áp dụng thuộc tính kiểu

1. Mở trang Genres.aspx trong thư mục Management ở chế độ Design. Chọn GridView và mở GridView Task, chọn Auto Format

Chọn Classic trong AutoFormat

Nhấn OK. GridView lúc này sẽ được cập nhật:

2. Thực thi trang Genres.aspx, kết quả:

3. Mở lại trang Genres.aspx trong chế độ Source chúng ta sẽ thấy một số mã định dạng mới được thêm vào phía trên và dưới phần tử <Columns>:


<AlternatingRowStyle BackColor="White" />

<Columns>

...

</Columns>

<EditRowStyle BackColor="#2461BF" />

<FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />

<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />

<PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />

<RowStyle BackColor="#EFF3FB" />

<SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />

<SortedAscendingCellStyle BackColor="#F5F7FB" />

<SortedAscendingHeaderStyle BackColor="#6D95E1" />

<SortedDescendingCellStyle BackColor="#E9EBEF" />

<SortedDescendingHeaderStyle BackColor="#4870BE" />

Kết hợp Styles, Themes và Skins

Các thuộc tính định dạng kiểu (styles), các themes và các skins chúng ta đã có dịp tìm hiểu từ các chương trước. Trong bài thực hành sau đây chúng ta sẽ tạo một tập tin skin cho phép định dạng các điều khiển GridView trong thư mục Management.

Thực hành định dạng kiểu (style) nâng cao

1. Thêm một theme mới đến thư mục App_Themes bằng cách nhấn chuột phải vào thư mục này và chọn Add > Add ASP.NET Folder > Theme và gõ tên theme mới là Management:

2. Thêm một tập tin skin đến thư mục theme mới Management bằng cách nhấn chuột phải vào Management chọn Add > Add New Item > Skin File và gõ tên tập tin skin là GridView.skin

3. Mở lại trang Genres.aspx (thư mục Management của Site) trong chế độ Source. Xóa các phần tử định dạng mới được thêm vào (trong phần Thực hành 1) ngoại trừ các phần tử HeaderStyle, PagerStyle, SortedAscendingHeaderStyle, và SortedDescendingHeaderStyle


<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 

   DataKeyNames="Id" DataSourceID="SqlDataSource1" 

   EmptyDataText="There are no data records to display." 

   AllowPaging="True" AllowSorting="True" CellPadding="4" 

   ForeColor="#333333" GridLines="None">

   <Columns>

   ...

   </Columns>

  <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />

  <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />

  <SortedAscendingHeaderStyle BackColor="#6D95E1" />

  <SortedDescendingHeaderStyle BackColor="#4870BE" />

</asp:GridView>

4. Xóa tất cả các thuộc tính của 4 phần tử giữ lại ở trên và thay bằng thuộc tính CssClass với các giá trị như sau:


<HeaderStyle CssClass="GridViewHeaderStyle" />

<PagerStyle CssClass="GridViewPagerStyle" />

<SortedAscendingHeaderStyle CssClass="GridViewSortedAscendingHeaderStyle" />

<SortedDescendingHeaderStyle CssClass="GridViewSortedDescendingHeaderStyle" />

5. Mở tập tin GridView.skin và xóa tất cả nội dung chú thích mặc định. Di chuyển 4 phần tử trên đến tập tin GridView.skin và đặt trong phần tử <asp:GridView>. Thay đổi các thuộc tính của <asp:GridView> như sau:


<asp:GridView runat="server" CssClass="GridView" >

  <HeaderStyle CssClass="GridViewHeaderStyle" />

  <PagerStyle CssClass="GridViewPagerStyle" />

  <SortedAscendingHeaderStyle CssClass="GridViewSortedAscendingHeaderStyle" />

  <SortedDescendingHeaderStyle CssClass="GridViewSortedDescendingHeaderStyle" />

</asp:GridView>

6. Thêm các ảnh và tập tin Management.css đến thư mục App_Themes

7. Mở tập tin Web.config trong thư mục Management (của Site), tìm đến phần tử <system.web> và tại phần tử Pages gõ Management cho thuộc tính theme:


<?xml version="1.0"?>

<configuration>

  <system.web>

    <pages theme="Management"/>

  </system.web>

</configuration>

8. Mở trang Management.master trong thư mục MasterPages ở chế độ Source, trong phần tử <head> dưới </asp:ContentPlaceHolder> thêm tập tin jQuery như sau:


<head runat="server">

  <title></title>

  <asp:ContentPlaceHolder id="head" runat="server">

  </asp:ContentPlaceHolder>

  <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" 

     data-wp-preserve="%3Cscript%20src%3D%22..%2FScripts%2Fjquery-3.3.1.min.js%22%3E%3C%2Fscript%3E" 

     data-mce-resize="false" data-mce-placeholder="1" 

     class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

</head>

9. Trở lại trang Genres.aspx trong chế độ Source. Tại phần tử <Columns> xóa phần tử <asp:BoundField DataField=”id”…> vì chúng ta không cần hiển thị ID đến người dùng và thay đổi các thuộc tính cho các phần tử khác như sau:


<Columns>

  <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" 

     ShowSelectButton="False" ItemStyle-Width="100px" />

  <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" 

     ItemStyle- Width="200px" />

  <asp:BoundField DataField="SortOrder" HeaderText="Sort Order" 

     SortExpression="SortOrder" />

</Columns>

10. Cuộn xuống cuối trang Genres.aspx thêm đoạn mã jQuery sau ngay phía trên phần tử </asp:Content>


<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" 

     data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%0A%24(function()%0A%0A%7B%0A%0A%24('.GridView%20tr%3Aodd%3Anot(.GridViewPagerStyle)').%0A%0AaddClass('GridViewAlternatingRowStyle')%3B%0A%0A%7D)%3B%0A%0A%3C%2Fscript%3E" 

     data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

</asp:Content>

11. Lưu tất cả và mở trang Genres.aspx trong trình duyệt, kết quả:

Chọn Manage Reviews và chọn thể loại Indie Rock:

Xử lý sự kiện (Handling Events)

Trong các chương trước chúng ta đã làm quen với các sự kiện từ đơn giản như sự kiện Click của Button đến các sự kiện phức tạp hơn như Inserting hay Inserted của các điều khiển SqlDataSource hay EntityDataSource. Chúng ta cũng đã tìm hiểu về các sự kiện của một trang ASPX bao gồm PreInit, Load, PreRender, và Unload. Vì biết các sự kiện và xử lý chúng hiệu quả là kiến thức quan trọng khi phát triển dự án ASP.NET, nên trong bài thực hành sau đây chúng ta sẽ cùng tìm hiểu trở lại các sự kiện và cách xử lý chúng, đặc biệt là các sự kiện từ các điều khiển kết buộc dữ liệu, thông qua xem xét chu kỳ sống của các điều khiển và trang.

Thực hành xem chu trình sống của điều khiển và trang

1. Thêm một trang mới tên Events.aspx trong thư mục Demos bằng cách nhấn chuột phải vào thư mục này và chọn Add > Add New Item

Chọn Frontend.master trong Select a Master Page

2. Mở tập tin Events.aspx.cs và điều chỉnh để lớp Demos_Events kế thừa từ lớp BasePage:


public partial class Demos_Events : BasePage

{

   protected void Page_Load(object sender, EventArgs e)

   {

   }

}

3. Mở lại trang Events.aspx trong chế độ Source và nhập giá trị là Events Demo cho thuộc tính Title với đến phần tử Page:


<%@ Page Title="Events Demo" Language="C#" MasterPageFile="~/MasterPages/Frontend.master" 

    AutoEventWireup="true" CodeFile="Events.aspx.cs" Inherits="Demos_Events" %>

4. Chuyển trang Events.aspx đến chế độ Design và thêm một GridView đến phần cpMainContent. Trong GridView Tasks, tại mục Choose Data Source chọn <New data source…>

Chọn Entity trong Data Source Configuration Wizard

Nhấn OK. Chọn ngocminhtranEntities tại Named Connection trong Configure ObjectContext

Nhấn Next. Chọn Genres tại EntitiesSetName trong Configure Data Selection

Nhấn Finish.

5. Trở lại GridView Tasks chọn Enable Sorting

6. Chuyển trang đến chế độ Source. Thêm đoạn mã HTML để tạo một bảng sau đây vào vị trí ngay dưới <asp:Content> và phía trên <asp:GridView>:


<asp:Content ID="Content2" ContentPlaceHolderID="cpMainContent" Runat="Server">

  <table>

     <tr>

      <td>

         <h1>No PostBack</h1>

         <asp:Label ID="NoPostBack" runat="server" />

      </td>

      <td>

         <h1>PostBack</h1>

         <asp:Label ID="PostBack" runat="server" />
      </td>

     </tr>

  </table>

  <asp:GridView ID="GridView1" runat="server" AllowSorting="True" ...>

  ...

  </asp:GridView>

  <asp:EntityDataSource ID="EntityDataSource1" ...>

  </asp:EntityDataSource>

</asp:Content>

7. Kéo một Button (từ ToolBox) và thả vào vị trí ngay dưới GridView ( dưới phần tử </asp:GridView>)

Chuyển trang sang chế độ Design:

Nhấn đôi chuột trái vào Button để đến thủ tục xử lý sự kiện Click của Button này trong tập tin Events.aspx.cs:


public partial class Demos_Events : BasePage

{

  protected void Page_Load(object sender, EventArgs e)

  {

  }

  protected void Button1_Click(object sender, EventArgs e)

  {

  }

}

8. Chuyển trở lại trang Events.aspx trong chế độ Design. Mở cửa sổ Properties của GridView và chuyển đến biểu tượng Events:

Nhấn đôi chuột để thêm thủ tục xử lý sự kiện Sorted cho GridView:


public partial class Demos_Events : BasePage

{

  protected void Page_Load(object sender, EventArgs e)
  {

  }

  protected void Button1_Click(object sender, EventArgs e)
  {

  }

  protected void GridView1_Sorted(object sender, EventArgs e)
  {

  }

}

Tương tự thêm các thủ tục xử lý sự kiện sau cho GridView:

  • Sorting
  • RowCreated
  • DataBinding
  • DataBound
  • RowDataBound

9. Thêm các thủ tục xử lý các sự kiện sau cho EntityDataSource (tương tự Button và GridView):

  • ContextCreating
  • Selecting

Kết quả trong tập tin Events.aspx.cs lúc này:


public partial class Demos_Events : BasePage
{

  protected void Page_Load(object sender, EventArgs e)
  {

  }

  protected void Button1_Click(object sender, EventArgs e)
  {

  }
 
  protected void GridView1_Sorted(object sender, EventArgs e)
  {

  }

  protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
  {

  }
  protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
  {

  }
  protected void GridView1_DataBinding(object sender, EventArgs e)
  {

  }
  protected void GridView1_DataBound(object sender, EventArgs e)
  {

  }

  protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
  {

  }

  protected void EntityDataSource1_ContextCreating(object sender,

          EntityDataSourceContextCreatingEventArgs e)
  {

  }

  protected void EntityDataSource1_Selecting(object sender,

        EntityDataSourceSelectingEventArgs e)

  {

  }

}

10. Thêm namespace sau đến tập tin Events.aspx.cs:


using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Runtime.CompilerServices;

public partial class Demos_Events : BasePage

{

  ...

}

11. Thêm phương thức WriteMessage hiển thị thông điệp phụ thuộc vào trang hiện tại yêu cầu là kết quả của một postback hay không ngay dưới thủ tục EntityDataSource1_Selecting:


private void WriteMessage([CallerMemberName] string handlerName = "")

{

  if (Page.IsPostBack)

  {

     PostBack.Text += handlerName + "<br />";

  }

  else

  {

    NoPostBack.Text += handlerName + "<br />";

  }

}

Tham số handlerName được khởi tạo giá trị rỗng và thuộc tính CallerMemberName sẽ chuyển tên của phương thức gọi WriteMessage làm giá trị cho tham số này.

12. Gọi phương thức WriteMessage từ các thủ tục xử lý các sự kiện của Page, Button, GridView và EntityDataSource. Kết quả tập tin Events.aspx.cs lúc này:


using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Runtime.CompilerServices;

public partial class Demos_Events : BasePage

{

  protected void Page_Load(object sender, EventArgs e)

  {

     WriteMessage();

  }

  protected void Button1_Click(object sender, EventArgs e)

  {

    WriteMessage();

  }

  protected void GridView1_Sorted(object sender, EventArgs e)

  {

    WriteMessage();

  }

  protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)

 {

    WriteMessage();

 }

 protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)

 {

   WriteMessage();

 }

 protected void GridView1_DataBinding(object sender, EventArgs e)

 {

  WriteMessage();

 }

 protected void GridView1_DataBound(object sender, EventArgs e)

 {

  WriteMessage();

 }

 protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)

 {

  WriteMessage();

 }

 protected void EntityDataSource1_ContextCreating(object sender,

        EntityDataSourceContextCreatingEventArgs e)

 {

   WriteMessage();

 }

 protected void EntityDataSource1_Selecting(object sender,

     EntityDataSourceSelectingEventArgs e)

 {

  WriteMessage();

 }

 private void WriteMessage([CallerMemberName] string handlerName = "")

 {

  if (Page.IsPostBack)

  {

    PostBack.Text += handlerName + "<br />";

  }

  else

  {

    NoPostBack.Text += handlerName + "<br />";

  }

 }

}

13. Cuối cùng thêm thủ tục xử lý sự kiện PreRenderComplete của trang (thêm trực tiếp) ngay dưới phương thức WriteMessage:

14. Lưu tất cả và thực thi trang Events.aspx trong trình duyệt, kết quả:

Chu kỳ sống của trang ASP.NET và các sự kiện của các data controls

Ở bài thực hành trên, chúng ta đã làm quen với các sự kiện RowCreatedRowDataBound được phát sinh khi một hàng được thêm vào GridView. Các sự kiện này hữu ích trong một số ví dụ như trường hợp chúng ta cần xác nhận một review đã được xác thực hay chưa hoặc trong trường hợp chúng ta cần ẩn hay vô hiệu hóa một số thành phần trong giao diện nếu chúng không cần thiết phải xuất hiện. Trong bài thực hành kế tiếp, chúng ta sẽ viết một trình xử lý cho sự kiện RowDataBound của GridView trong trang Genres.aspx của thư mục Management. Sự kiện này cho phép chúng ta kiểm tra một genre nào đó có gán các reviews hay chưa, nếu genre được gán reviews thì liên kết Delete sẽ được ẩn để người dùng không thể xóa các genre này.

Thực hành với sự kiện RowDataBound

1. Mở trang Genres.aspx trong thư mục Management ở chế độ Source và tìm đến điều khiển SqlDataSource. Tìm đến lệnh SelecCommand và thay đổi lại nội dung như sau:


<asp:SqlDataSource ID="SqlDataSource1" runat="server" 

     ConnectionString="<%$ ConnectionStrings:ngocminhtranConnectionString1 %>"

  DeleteCommand= ...

  InsertCommand= ...

  ProviderName= ...

  SelectCommand= "SELECT Genre.Id, Genre.Name, Genre.SortOrder,

        COUNT(Review.Id) AS NumberOfReviews 

        FROM Genre LEFT OUTER JOIN Review

        ON Genre.Id = Review.GenreId 

        GROUP BY Genre.Id, Genre.Name, Genre.SortOrder"

  UpdateCommand= ...

2. Chuyển trang sang chế độ Design và mở thanh GidView Tasks của GridView. Nhấn vào liên kết Refresh Schema và chọn No nếu có một thông báo xuất hiện. Kế tiếp chọn Edit Columns mở hộp thoại Fields, chọn CommandField trong Selected fields và nhấn vào liên kết Convert this field into a TemplateFiled và nhấn OK:

3. Mở trang Genres.aspx lại chế độ Source và tìm đến phần tử LinkButton có thuộc tính CommandName với giá trị là Delete:


<asp:LinkButton ID="LinkButton2" runat="server" CausesValidation="False" 

    CommandName="Delete" Text="Delete"></asp:LinkButton>

Thay đổi ID là DeleteLink


<asp:LinkButton ID="DeleteLink" runat="server" CausesValidation="False" 

       CommandName="Delete" Text="Delete"></asp:LinkButton>

4. Trở lại chế độ Design, mở hộp thoại Properties của GridView, chọn Events (biểu tượng tia chớp) và nhấn vào sự kiện RowDataBound. Thêm đoạn mã sau đến phương thức GridView1_RowDataBound:


protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)

{

  switch (e.Row.RowType)

  {

    case DataControlRowType.DataRow:

        DataRowView myDataRowView = (DataRowView)e.Row.DataItem;

        if (Convert.ToInt32(myDataRowView["NumberOfReviews"]) > 0)

        {

         LinkButton deleteLink = e.Row.FindControl("DeleteLink") as LinkButton;

         if (deleteLink != null)

         {

          deleteLink.Enabled = false;

         }

       }

       break;

   }

}

Lưu ý chúng ta cần khai báo namespace


using System.Data;

5. Lưu và thực thi trang Genres.aspx

Lưu ý, một số genre như Pop, Hard Rock, Rock, Jazz vì được gán các review nên các liên kết Delete bị vô hiệu hóa (bị mờ đi).

Caching

Caching là một trong những cách được sử dụng thường xuyên và hiệu quả nhất bởi các nhà phát triển trong việc cải thiện hiệu suất của một ứng dụng. Với caching, một bản sao dữ liệu của chúng ta sẽ được lưu trữ tại một vị trí đặc biệt gọi là bộ nhớ cache để có thể truy cập một cách nhanh chóng hơn rất nhiều so với việc phát sinh lại dữ liệu hay lấy dữ liệu trực tiếp từ bộ nhớ nguồn.

Các bước cơ bản của caching gồm: Đầu tiên, ứng dụng sẽ truy vấn một vài dữ liệu từ cơ sở dữ liệu, ví dụ danh sách các genres. Thay vì truy cập trực tiếp đến cơ sở dữ liệu, ứng dụng sẽ kiểm tra bộ nhớ cache xem dữ liệu có tồn tại hay không. Nếu dữ liệu truy vấn tồn tại trong cache, nó sẽ được trả về trực tiếp từ bộ nhớ cache. Ngược lại, nếu dữ liệu truy vấn không tồn tại trong cache, nó sẽ được nhận từ cơ sở dữ liệu và một bản sao của dữ liệu này sẽ được lưu trữ trong cache để phục vụ cho lần truy vấn kế tiếp. Minh họa các bước như hình sau:

Tuy nhiên, khi làm việc với bộ nhớ cache chúng ta thường mắc hai sai lầm:

  • Giả sử dữ liệu có tồn tại trong cache thì chúng cũng đã quá cũ. Giải pháp tránh dữ liệu cũ trong cache có hai cách: Cách thứ nhất là thiết lập thời gian tồn tại của dữ liệu trong cache; nếu vượt quá thời gian cho phép, dữ liệu sẽ bị hủy. Cách thứ hai là thiết lập sự phụ thuộc giữa dữ liệu trong cache và dữ liệu nguồn; nếu dữ liệu nguồn thay đổi thì dữ liệu trong cache cũng sẽ bị hủy và sẽ được cập nhật trong lần yêu cầu kế tiếp.
  • Chúng ta thường giả định dữ liệu chúng ta cần có tồn tại trong bộ nhớ cache, trong khi thực tế lại không. Do đó, cần kiểm tra một cách cẩn thận trước khi truy cập bộ nhớ cache.

Với ứng dụng ASP.NET, có 3 cách sử dụng cơ chế caching như sau:

Output Caching

Output Caching có nghĩa rằng, khi trang được yêu cầu lần đầu tiên, kết quả trả về cuối cùng của nó được lưu trong bộ nhớ cache. Khi trang này được yêu cầu trong những lần kế tiếp, nội dung trả về giống với nội dung đã lưu trong cache ở lần yêu cầu đầu tiên, tức là, trang sẽ không được xử lý tại server trở lại.

Áp dụng Output Caching cho một trang nào đó bằng cách thêm chỉ thị OutputCache ngay phía dưới chỉ thị của trang đó, ví dụ:


<%@ Page Title="About this Site" Language="C#" 

    MasterPageFile="~/MasterPages/Frontend.master" AutoEventWireup="true" 

    CodeFile="Default.aspx.cs" Inherits="About_Default" %>

    <%@ OutputCache Duration="60" VaryByParam="None" %>

Tham số Duration xác định thời gian (giây) tồn tại của dữ liệu trong cache; VaryByParam được thiết lập đến giá trị None để thông báo .NET lưu đến bộ nhớ cache một phiên bản đơn của trang mà không cần quan tâm đến giá trị của chuỗi kết nối. Sử dụng giá trị None cho một vài trang đặc trưng, ví dụ trang About, thì rất hiệu quả nhưng trong nhiều trường hợp thì đây không phải là giải pháp lý tưởng. Một ví dụ đơn giản là giả sử rằng chúng ta cần biết thông tin về review có Id là 23 thông qua truy vấn:


http://localhost:12345/Reviews/ViewDetails.aspx?Id=23

Với truy vấn này, trang chứa toàn bộ thông tin về rewiew 23 sẽ hiển thị và trang này cũng được lưu đến bộ nhớ cache. Giả sử, trong lần truy cập kế tiếp, chúng ta dùng truy vấn sau:


http://localhost:12345/Reviews/ViewDetails.aspx?Id=33

Truy vấn trên yêu cầu xem nội dung review 33 nhưng thay vì hiển thị nội review 33, chúng ta sẽ nhận được thông tin từ review 23.

Để khắc phục tình trạng trên, ASP.NET cho phép chúng ta lưu trữ các trang cụ thể đến bộ nhớ cache như sau:


<%@ OutputCache Duration="60" VaryByParam="Id" %>

Lúc này giá trị của VaryByParam là Id của trang cụ thể.

Việc dùng Output Caching là khá dễ dàng nhưng vấn đề chính của cách thức này liên quan đến bảo mật thông tin khi dùng một số điều khiển liên quan như LoginView. Một vấn đề khác là việc lưu toàn bộ nội dung trang đến cache là không cần thiết. Để khắc phụ những vấn đề này, Chúng ta sẽ cùng tìm hiểu cách thứ hai trong Caching.

 Caching với các điều khiển Data Source

Ưu điểm chính của cách này là chỉ một phần nội dung trang liên quan đến cơ sở dữ liệu được lưu đến bộ nhớ cache. Các phần tĩnh khác như banner, các slogan, v.v. không cần thiết phải lưu. Phần lớn các điều khiển Data Source hỗ trợ cách caching ngoại trừ các điều khiển SiteMapDataSource, LinqDataSource, và EntityDataSource.

Để sử dụng cách caching này, chúng ta chỉ cần thiết lập giá trị true cho thuộc tính EnableCaching và quy định khoảng thời gian (giây) cho thuộc tính CacheDuration cho điều khiển tương ứng, ví dụ SqlDataSource, như sau:


<asp:SqlDataSource ID="SqlDataSource1" runat="server" 

     CacheDuration="600" EnableCaching="True"></asp:SqlDataSource>

Cách thức caching này khá hiệu quả nhưng chỉ áp dụng cho các điều khiển Data Source. Trong các trường hợp sử dụng các điều khiển khác cách này không áp dụng được. Lúc này, cách thứ 3 phức tạp nhất, linh động nhất và có thể áp dụng cho mọi điều khiển sẽ được áp dụng.

Lập trình để Caching

Sử dụng cách caching này đòi hỏi kỹ năng lập trình và am hiểu các phương thức Caching trong ASP.NET. Để có thể hình dung cách thức caching này, chúng ta sẽ tiến hành phần thực hành sau đây.

Thực hành dùng Cache API

1. Trong thư mục Reviews tạo một trang tên ViewDetails.aspx thừa kế từ template BasePage hay trang Master. Mở trang trong chế độ Source và xóa thuộc tính Title để tránh phát sinh lỗi.

2. Thêm 3 điều khiển Label với ID lần lượt là TitleLabel, SummaryLabel và BodyLabel đến phần cpMainContent. Xóa thuộc tính Text, đặt TitleLabel trong phần tử <h1> và thêm thuộc tính CssClass với giá trị là Summary đến SummaryLabel:


asp:Content ID="Content2" ContentPlaceHolderID="cpMainContent" Runat="Server">

  <h1><asp:Label ID="TitleLabel" runat="server"></asp:Label></h1>

  <asp:Label CssClass="Summary" ID="SummaryLabel" runat="server"></asp:Label>

  <asp:Label ID="BodyLabel" runat="server"></asp:Label>

</asp:Content>

3. Mở tập tin ViewDetails.aspx.cs tìm đến phương thức Page_Load


using ngocminhtranModel;

protected void Page_Load(object sender, EventArgs e)

{

  int reviewId = Convert.ToInt32(Request.QueryString.Get("ReviewId"));

  string cacheKey = "Reviews" + reviewId.ToString();

  Review myReview = Cache[cacheKey] as Review;

  if (myReview == null)

  {

    using (ngocminhtranEntities myEntities = new ngocminhtranEntities())

    {

      myReview = (from r in myEntities.Reviews

                   where r.Id == reviewId

                   select r).SingleOrDefault();

      if (myReview != null)

      {

        Cache.Insert(cacheKey, myReview, null, DateTime.Now.AddMinutes(20),

                      System.Web.Caching.Cache.NoSlidingExpiration);

      }

    }

  }

  if (myReview != null)

  {

    TitleLabel.Text = myReview.Title;

    SummaryLabel.Text = myReview.Summary;

    BodyLabel.Text = myReview.Body;

    Title = myReview.Title;

    MetaDescription = myReview.Summary;

  }

}

4. Mở trang All.aspx trong thư mục Reviews và xóa điều khiển GridView chúng ta đã tạo trong bài thực hành trước thay bằng một Repeater chứa Hyperlink như sau:


<asp:Content ID="Content2" ContentPlaceHolderID="cpMainContent" Runat="Server">

  <asp:Repeater ID="Repeater1" runat="server" ItemType="PlanetWroxModel.Review">

   <ItemTemplate>

     <asp:HyperLink ID="HyperLink1" runat="server"

        NavigateUrl='<%# "ViewDetails.aspx?ReviewId=" + Item.Id.ToString() %>'

        Text='<%# Item.Title %>'></asp:HyperLink>

   </ItemTemplate>

   <SeparatorTemplate></SeparatorTemplate>

  </asp:Repeater>

</asp:Content>

5. Mở tập tin All.aspx.cs và thay thế hai đoạn mã dùng GridView1 bằng hai đoạn mã dùng Repeater như sau:


Repeater1.DataSource = authorizedReviews;

Repeater1.DataBind();

6. Khai báo lớp CSS Summary chúng ta dùng cho SummaryLabel ở trên tại phần cuối của hai tập tin Monochrome.css và DarkGrey.css:


.Summary {

  font-style: italic;

  display: block;

}

7. Lưu tất cả. Mở trang All.aspx chúng ta sẽ thấy tiêu đề các review hiển thị trên trang

Nhấn chuột vào một tiêu đề bất kỳ sẽ đến trang ViewDetails.aspx hiển thị chi tiết thông tin về review:

Lúc này, nếu chúng ta nhấn Ctrl + F5 hay Ctrl + R để refresh lại trang thì nội dung vẫn thế chỉ có điều nội dung này được lấy từ bộ nhớ cache. Có thể kiểm tra điều này bằng cách thêm một Label đến phần cpMainContent của trangViewDetails.aspx ( đặt dưới 3 Label chúng ta đã thêm ở trên) như sau:


<h1><asp:Label ID="Label1" runat="server"></asp:Label></h1>

Thêm hai dòng mã đến tập tin ViewDetails.aspx.cs như sau:


Review myReview = Cache[cacheKey] as Review;

Label1.Text = "In the cache";

if (myReview == null)

{

  Label1.Text = "NOT In the cache";

  using (ngocminhtranEntities1 myEntities = new ngocminhtranEntities1())

  {

   ...

Thực thi lại trang All.aspx và nhấn vào một review bất kỳ, trang ViewDetails.aspx có thể như sau:

Nhấn Ctrl + F5 (hay Ctrl + R) kết quả không đổi. Đóng ứng dụng.

Tổng kết

Trong chương này chúng ta đã khám phá một vài cách nâng cao trong xử lý dữ liệu như định dạng, xử lý sự kiện hay caching. Một vấn đề quan trọng khác liên quan đến dữ liệu là bảo mật sẽ được tìm hiểu trong chương sau.