[LINQ] Phần 1: Giới thiệu về LINQ

418 lượt xem

Một truy vấn là một diễn tả để lấy dữ liệu từ 1 nguồn dữ liệu nào đó. Các truy vấn thường được dùng trong các ngôn ngữ truy vấn đặc trưng. Các ngôn ngữ truy vấn khác nhau được phát triển khác nhau để áp dụng cho các nguồn dữ liệu khác nhau, ví dụ SQL dùng cho cơ sở dữ liệu quan hệ và XQuery dùng cho XML. Vì vậy, các nhà phát triển phải học các ngôn ngữ truy vấn cho mỗi dạng nguồn dữ liệu hoặc định dạng dữ liệu. Do đó, Microsoft mới nghĩ ra việc xây dựng 1 ngôn ngữ truy vấn nào đó mà có thể dùng cho tất cả nguồn dữ liệu, tất cả các định dạng dữ liệu khác nhau. Ngôn ngữ truy vấn đó là LINQ.

LINQ (Language Integrated Query, tạm dịch là ngôn ngữ truy vấn tích hợp) đưa ra 1 mô hình bền vững để hoạt động với các dạng nguồn dữ liệu và định dạng dữ liệu khác nhau. Trong LINQ, bạn phải làm quen với chuyện làm việc với các đối tượng (objects). LINQ cho phép dùng các đoạn code đơn giản để truy vấn và chuyển đổi dữ liệu trong các tài liệu XML, cơ sở dữ liệu SQL, tập dữ liệu ADO.NET, các tập hợp .NET, và bất kỳ định dạng nào mà LINQ provider hỗ trợ.

Ba thành phần của 1 hoạt động truy vấn
Tất cả các hoạt động truy vấn LINQ đều bao gồm 3 tác vụ:

  • Kết nối với nguồn dữ liệu (data source)
  • Tạo truy vấn
  • Thực thi truy vấn

Ví dụ sau đây mô tả cách 3 tác vụ xảy ra khi thực hiện 1 hoạt động truy vấn. Để thuận tiện, ví dụ sử dụng 1 mảng số nguyên là nguồn dữ liệu; tuy nhiên bạn có thể dùng các dạng nguồn dữ liệu khác.

class IntroToLINQ
    {        
        static void Main()
        {
            // Ba thành phần của 1 truy vấn LINQ:
            //  1. Nguồn dữ liệu
            int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

            // 2. Tạo truy vấn
            // numQuery là 1 IEnumerable<int>
            var numQuery =
                from num in numbers
                where (num % 2) == 0
                select num;

            // 3. Truy vấn dữ liệu
            foreach (int num in numQuery)
            {
                Console.Write("{0,1} ", num);
            }
        }
    }

Chúng ta cùng phân tích kỹ lại ví dụ trên:

  • Nguồn dữ liệu trong ví dụ là mảng số nguyên tên là numbers gồm 7 phần tử có giá trị là 0, 1, 2, 3, 4, 5, 6.
  • Câu truy vấn được tạo bằng cách sử dụng 1 diễn giải Lambda (lambda expression), gần giống như ngôn ngữ truy vấn SQL. Trong trường hợp này chúng ta tạo 1 biến numQuery với kiểu var (kiểu không xác định kiểu dữ liệu). Câu diễn giải này (from num in numbers where (num % 2) == 0 select num;) tức là chúng ta chọn tất cả các số trong mảng numbers chia hết cho 2. Sau khi khai báo diễn giải Lambda cho biến numQuery thì ở đây chúng ta có thể hiểu numQuery là 1 danh sách các số chẵn (chia hết cho 2), vì vậy kiểu dữ liệu ngầm của numQuery chính là IEnumerable. IEnumerable là 1 lớp giao diện cơ bản cho tất cả các tập hợp cho thể được liệt kê. Cách dùng phổ biến của IENumerable thường là System.Collections.Generic.IEnumerable<T> với T là kiểu dữ liệu của mỗi đối tượng trong danh sách.
  • Vì biến numQuery dùng diễn giải Lambda cho nên kiểu dữ liệu của nó chính là 1 danh sách đối tượng các giá trị chia hết cho 2. Do đó, chúng ta có thể dùng 1 vòng foreach để truy vấn các giá trị phần tử này và in ra màn hình.

Ví dụ trên có thể mô tả bằng hình ảnh như sau:
ic154187

Data Source (nguồn dữ liệu)
Trong ví dụ trên, bởi vì nguồn dữ liệu là 1 mảng cho nên nó cũng hỗ trợ ngầm lớp giao diện chung IEnumerable<T>. Rõ hơn là, nguồn dữ liệu này có thể được truy vấn bằng LINQ. Một truy vấn thực thi bằng 1 mệnh đề foreach, và foreach cần IEnumerable<T> hay IEnumerable. IEnumerable là 1 giao diện cơ bản hỗ trợ cho tất cả các tập hợp không chung nhau (non-generic) mà có thể được liệt kê, trong khi đó IEnumerable là giao diện dành cho các tập hợp chung nhau (generic). Các dạng hỗ trợ IEnumerable<T> hoặc giao diện kế thừa như IQueryable<T> (generic) gọi là các dạng có khả năng truy vấn. Một dạng có khả năng truy vấn (queryable type) không cần các thay đổi hay xử lý đặc biệt để đóng vai trò là 1 nguồn dữ liệu LINQ. Nếu nguồn dữ liệu không sẵn có trong bộ nhớ dưới dạng có khả năng truy vấn, LINQ provider phải làm sao thể hiện nó như vậy (tức là phải là dạng có khả năng truy vấn). Ví dụ sau mô tả cách LINQ chuyển thành XML để tải nội dung 1 tài liệu XML thành dạng có khả năng truy vấn XElement.

// Tạo nguồn dữ liệu từ 1 tập tin XML.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

Với LINQ sang SQL, đầu tiên chúng ta phải tạo 1 ánh xạ quan hệ đối tượng lúc thiết kế bằng tay hoặc sử dụng công cụ chuyển LINQ thành SQL ở trong Visual Studio. Sau đó viết câu truy vấn các đối tượng và xử lý các truy vấn này lúc tương tác với database. Ví dụ sau, bảng Customers là 1 bảng trong database, và dạng kết quả truy vấn IQueryable<T> kế thừa từ IEnumerable<T>.

Northwnd db = new Northwnd(@"c:\northwnd.mdf");  
// Truy vấn các khách hàng ở thành phố Đà Lạt
IQueryable<Customer> custQuery =  
    from cust in db.Customers  
    where cust.City == "Đà Lạt"  
    select cust;  

Query (câu truy vấn)
Câu truy vấn mô tả cách thông tin được rút trích từ nguồn dữ liệu hay các nguồn. Thêm nữa, câu query cũng có thể mô tả cách thông tin được sắp xếp, gom nhóm và thay đổi trước khi trả về. Một câu truy vấn được chứa trong 1 biến truy vấn và được bắt đầu bằng 1 diễn giải truy vấn (query expression). Để thuận cho việc viết truy vấn, C# giới thiệu cú pháp truy vấn mới. Truy vấn trong ví dụ trước trả về tất cả các số chẵn từ 1 mảng số nguyên. Diễn giải truy vấn chứa 3 mệnh đề: from, where và select.. Nếu bạn quen thuộc với SQL, bạn có thể chú ý cách thứ tự các mệnh đề ở vị trí đảo ngược so với các truy vấn trong SQL. Mệnh đề from mô tả nguồn dữ liệu, mệnh đề where mô tả việc lọc thông tin và mệnh đề select mô tả các dạng phần tử thông tin cần trả về.

Query Execution (thực thi truy vấn)
Trong phần này, chúng ta sẽ tìm hiểu các dạng thực thi truy vấn trong LINQ.

Thực thi trì hoãn
Biến truy vấn chỉ lưu trữ chính nó các lệnh truy vấn. Các thực thi thực sự của truy vấn được trì hoãn cho đến khi chúng ta lặp qua các biến truy vấn trong mệnh đề foreach. Khái niệm này được gọi là 1 thực thi trì hoãn và được biểu diễn thông qua ví dụ sau:

//  Thực thi truy vấn
            foreach (int num in numQuery)
            {
                Console.Write("{0,1} ", num);
            }

Mệnh đề foreach cũng là nơi kết quả truy vấn được rút trích. Trong ví dụ trước, biến lặp num giữ giá trị mỗi phần tử (tại từng thời điểm) trong chuỗi trả về. Bởi vì biến truy vấn không giữ các kết quả truy vấn, vì vậy chúng ta có thực thi chúng nhiều lần nếu muốn.

Thực thi ép buộc ngay lập tức (Forcing Immediate Execution)
Các truy vấn thực thi các hàm kết hợp trên 1 dãy các yếu tố nguồn phải lặp trước trên từng yếu tố đó, chẳng hạn các truy vấn như Count, Max, Average và First. Những thực thi này không cần mệnh đề foreach minh bạch vì truy vấn chính nó phải dùng foreach để trả về kết quả. Lưu ý các dạng truy vấn trả về 1 biến đơn, không phải 1 tập hợp IEnumerable. Truy vấn sau trả về biến đếm tổng số các số trong 1 mảng nguồn.

var evenNumQuery = 
                from num in numbers
                where (num % 2) == 0
                select num;

// Đếm tổng các số chia hết cho 2 được trả về kiểu biến không xác định evenNumQuery
int evenNumCount = evenNumQuery.Count();

Để thực thi ép buộc ngay lập tức bất kỳ truy vấn nào và lấy dữ liệu của nó, chúng ta có thể dùng 2 phương thức ToList<TSource> hoặc ToArray<TSource>.

            List<int> numQuery2 =
                (from num in numbers
                 where (num % 2) == 0
                 select num).ToList();

            // hoặc như cách
            // numQuery3 vẫn là 1 mảng int[]

            var numQuery3 =
                (from num in numbers
                 where (num % 2) == 0
                 select num).ToArray();

Chúng ta cũng có thể ép buộc thực thi bằng cách đặt các vòng foreach ngay lập tức sau các diễn giải truy vấn, tuy nhiên bằng cách gọi ToList hay ToArray, chúng ta cũng lưu tất cả dữ liệu trong 1 đối tượng tập hợp đơn.

Kết luận: bài viết giới thiệu tổng quan về LINQ và chi tiết cách LINQ thực thi thông qua các ví dụ cụ thể.


Vui lòng trích dẫn địa chỉ website nếu sao chép hoặc tổng hợp thông tin từ website này.

Bình luận Facebook

Để lại bình luận

Hãy là người đầu tiên bình luận!

Thông báo khi có
avatar
1000
wpDiscuz