Thứ Hai, 5 tháng 8, 2013

Truy cập nhanh cơ sở dữ liệu với Data Reader (Lập trình C# )

Một data reader là cách đơn giản nhất và nhanh nhất để chọn một vài dữ liệu từ một nguồn cơ sơ dữ liệu, nhưng cũng ít tính năng nhất. Bạn có thể truy xuất trực tiếp một đối tượng data reader – Một minh dụ được trả về từ một đối tượng SqlCommand hoặc OleDbCommand từ việc gọi một phương thức ExecuteReader()
– có thể là một đối tượng SqlCommand, một đối tượng SqlDataReader, từ một đối tượng OleDbCommand là một OleDbDataReader.
Mã lệnh sau đây sẽ chứng minh cách chọn dữ liệu từ bản Customers của cơ sở dữ liệu Northwind. Ví dụ kết nối với cơ sở dữ liệu chọn một số các mẫu tin, duyệt qua các mẫu tin được chọn và xuất chúng ra màn hình console.
Ví dụ này có thể dùng cho OLE DB provider. Trong hầu hết các trường hợp các phương thức của SqlClient đều được ánh xạ một một vào các phương thức của đối OleDBClient.
Để thực thi lại các lệnh đối với một OLE DB data source, lớp OleDbCommand được sử dụng. Mã lệnh dưới đây là một ví dụ một câu lệnh SQL đơn giảnvà đọc các mẫu tin được trả về bởi đối tượng OleDbDataReader.
Mã của ví dụ có thể được tìm thấy trong thư mục Chapter 09\03_DataReader.
Chú ý hai câu lệnh using dưới đây được dùng trong lớp OleDb:
using System;
using System.Data.OleDb;
Tất cả các trình cung cấp dữ liệu đều sẵn chứa bên trong các data DLL, vì vậy chỉ cần tham chiếu đến System.Data.dll assembly để dùng cho các lớp trong phần này:
public class DataReaderExample
{
   public static void Main(string[] args)
   {
      string source = "Provider=SQLOLEDB;" +
                      "server=(local)\\NetSDK;" +
                      "uid=QSUser;pwd=QSPassword;" + 
                      "database=northwind";
      string select = "SELECT ContactName,CompanyName FROM Customers";
      OleDbConnection conn = new OleDbConnection(source);
      conn.Open();
      OleDbCommand cmd = new OleDbCommand(select , conn);
      OleDbDataReader aReader = cmd.ExecuteReader();
      while(aReader.Read())
         Console.WriteLine("'{0}' from {1}" , 
                           aReader.GetString(0) , aReader.GetString(1));
      aReader.Close();
      conn.Close();
   }
}
Mã nguôn trên đây bao gồm các đoạn lệnh quen thuộc đã được trình bày trong các chương trước. Để biên dịch ví dụ này, ta dùng các dòng lệnh sau:
csc /t:exe /debug+ DataReaderExample.cs /r:System.Data.dll
Mã sau đây từ ví dụ trên cho phép tạo một kết nối OLE DB .NET, dựa trên chuỗi kết nối:
   OleDbConnection conn = new OleDbConnection(source);
   conn.Open();
   OleDbCommand cmd = new OleDbCommand(select, conn);
Dòng thứ ba tạo một đối tượng OleDbCommand mới, dựa vào câu lệnh SELECT, kết nối sẽ thực thi câu lệnh lệnh này. Nếu bạn tạo một command hợp lệ, bạn có thể thực thi chúng để trả về một minh dụ OleDbDataReader:
   OleDbDataReader aReader = cmd.ExecuteReader();
Mội OleDbDataReader chỉ là một con trỏ "connected" định trước. Mặt khác, bạn có thể chỉ duyệt qua các mẫu tin được trả về, kết nối hiện tạo sẽ lưu giữ các mẫu tin đó cho đến khi data reader bị đóng lại.
Lớp OleDbDataReader không thể tạo minh dụ một cách trực tiếp – nó luôn được trả về thông qua việc gọi phương thức ExecuteReader() của lớp OleDbCommand. Nhưng bạn có thể mở một data reader, có một số cách khác nhau để truy cập dữ liệu trong reader.
Khi một đối tượng OleDbDataReader bị đóng lại (thông qua ciệc gọi phương thức Close(), hoặc một đợt thu dọn rác), kết nối bên dưới có thể bị đóng lại thông qua một lời gọi phương thức ExecuteReader(). Nếu bạn gọi ExecuteReader() và truyền CommandBehavior.CloseConnection, bạn có thể ép kết nối đóng lại khi đóng reader.
Lớp OleDbDataReader có một bộ các quyền truy xuất thông qua các mảng quen thuộc:
   object o = aReader[0];
   object o = aReader["CategoryID"];
Ở đây CategoryID là trường đầu tiên trong câu lệnh SELECT của reader, cả hai dòng trên đều thực hiện công việc giống nhau tuy nhiên cách hai hơi chậm hơn cách một – Tôi đã viết một ứng dụng đơn giản để thực thi việc lập lại quá trình truy cập cho hàng triệu lần một cột trong một mẫu tin reader, chỉ để lấy một vài mẫu. Tôi biết bạn hầu như không bao giờ đọc một cột giống nhau hàng triệu lần, nhưng có thể là một số lần, bạn nên viết mã để tối ưu quá trình đó.
Bạn có biết kết quả là thế nào không, việc truy cập môt triệu lần bằng số thứ tự chỉ tốn có 0.09 giây, còn dùng chuỗi kí tự phải mất 0.63 giây. Lí do của sự chậm trễ này là vì khi dùng chuỗi kí tự ta phải dò trong schema để lấy ra số thứ tự của cột từ đó mới truy xuất được cơ sở dữ liệu. Nếu bạn biết được các thông tin này bạn có thể viết mã truy xuất dữ liệu tốt hơn.
Vì vậy việc dùng chỉ số cột là cách dùng tốt nhất.
Hơn thế nữa, OleDbDataReader có một bộ các phương thức type-safe có thể dùng để đọc các cột. Những phương thức này có thể đọc hầu hết các loại dữ liệu như GetInt32, GetFloat, GetGuid, vân vân.
Thí nghiệm của tôi khi dùng GetInt32 là 0.06 giây. Nhanh hơn việc dùng chỉ số cột, vì khi đó bạn phải thực hiện thao tác ép kiểu để đưa kiểu trả về kiểu integer. Vì vậy nếu biết trước schema bạn nên dùng các chỉ số thay vì tên.
Chắc bạn cũng biết nên giữ sự cân bằng giữa tính dễ bảo trì và tốc độ. Nếu bạn muốn dùng các chỉ mục, bạn nên định nghĩa các hằng số cho mỗi cột mà bạn sẽ truy cập.
Ví dụ dưới đây giống như ví dụ ở trên nhưng thay vì sử dụng OLE DB provider thì ở đây sử dụng SQL provider. Nhưng phần thay đổi của mã so với ví dụ trên được tô đậm. Ví dụ này nằm trong thư mục 04_DataReaderSql:
using System;
using System.Data.SqlClient;
public class DataReaderSql
{
   public static int Main(string[] args)
   {
      string source = "server=(local)\\NetSDK;" +
                      "uid=QSUser;pwd=QSPassword;" + 
                      "database=northwind";
      string select = "SELECT ContactName,CompanyName FROM Customers";
      SqlConnection conn = new SqlConnection(source);
      conn.Open();
      SqlCommand cmd = new SqlCommand(select , conn);
      SqlDataReader aReader = cmd.ExecuteReader();
      while(aReader.Read())
         Console.WriteLine("'{0}' from {1}" , aReader.GetString(0) ,
                            aReader.GetString(1));
      aReader.Close();
      conn.Close();
      return 0;
   }
}
Tôi đã chạy thử nghiệm của mình trên SQL provider, và kết quả là 0.13 giây cho một triệu lần truy cập bằng chỉ mục, và 0.65 giây nếu dùng chuỗi. Bạn có mong rằng SQL Server provider nhanh hơn so với OleDb, tôi đã test thử nghiệm của mình trong phiên bản .NET.
Nếu bạn có hứng thú chạy mã này trên máy tính của bạn thì nó nằm trong các ví dụ 05_IndexerTestingOleDb06_IndexerTestingSql trong mã bạn đã down về.