C# - C Sharp: Giới thiệu về truy vấn LINQ


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Truy vấn (Query) là một biểu thức truy xuất dữ liệu từ nguồn dữ liệu. Các truy vấn thường được thể hiện bằng một ngôn ngữ truy vấn chuyên biệt. Các ngôn ngữ khác nhau đã được phát triển theo thời gian cho nhiều loại nguồn dữ liệu khác nhau, ví dụ SQL cho cơ sở dữ liệu quan hệ và XQuery cho XML. Do đó, các nhà phát triển đã phải học một ngôn ngữ truy vấn mới cho từng loại nguồn dữ liệu hoặc định dạng dữ liệu mà họ phải hỗ trợ. LINQ đơn giản hóa tình huống này bằng cách cung cấp một mô hình nhất quán để làm việc với dữ liệu trên nhiều loại nguồn và định dạng dữ liệu khác nhau. Trong truy vấn LINQ, bạn luôn làm việc với các đối tượng. Bạn sử dụng các mẫu mã hóa cơ bản giống nhau để truy vấn và chuyển đổi dữ liệu trong các tài liệu XML, cơ sở dữ liệu SQL, Bộ dữ liệu ADO.NET, collection .NET và bất kỳ định dạng nào khác mà nhà cung cấp LINQ có sẵn.

Ba phần của thao tác truy vấn

Tất cả các hoạt động truy vấn LINQ bao gồm ba hành động riêng biệt:

  1. Lấy nguồn dữ liệu.
  2. Tạo truy vấn.
  3. Thực hiện truy vấn.

Ví dụ sau đây cho thấy ba phần của thao tác truy vấn được thể hiện như thế nào trong mã nguồn. Ví dụ này sử dụng một mảng số nguyên làm nguồn dữ liệu để thuận tiện; tuy nhiên, các khái niệm tương tự cũng áp dụng cho các nguồn dữ liệu khác. Ví dụ này được đề cập đến trong suốt phần còn lại của bài viết này.

class IntroToLINQ
{
    static void Main()
    {
        // The Three Parts of a LINQ Query:
        // 1. Data source.
        int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

        // 2. Query creation.
        // numQuery is an IEnumerable<int>
        var numQuery =
            from num in numbers
            where (num % 2) == 0
            select num;

        // 3. Query execution.
        foreach (int num in numQuery)
        {
            Console.Write("{0,1} ", num);
        }
    }
}

Hình minh họa sau đây cho thấy thao tác truy vấn hoàn chỉnh. Trong LINQ, việc thực hiện truy vấn khác với chính truy vấn đó. Nói cách khác, bạn chưa truy xuất được bất kỳ dữ liệu nào chỉ bằng cách tạo một biến truy vấn.

Sơ đồ hoạt động truy vấn LINQ hoàn chỉnh.

Nguồn dữ liệu

Trong ví dụ trên, vì nguồn dữ liệu là một mảng nên nó hoàn toàn hỗ trợ interface IEnumerable<T> generic. Thực tế này có nghĩa là nó có thể được truy vấn bằng LINQ. Một truy vấn được thực thi trong một câu lệnh foreach và foreach sẽ yêu cầu IEnumerable hoặc IEnumerable<T>. Các kiểu mà hỗ trợ IEnumerable<T> hoặc interface dẫn xuất chẳng hạn như IQueryable<T> generic được gọi là các kiểu có thể truy vấn.

Kiểu có thể truy vấn không cần sửa đổi hoặc xử lý đặc biệt để phục vụ như nguồn dữ liệu LINQ. Nếu dữ liệu nguồn chưa có sẵn trong bộ nhớ dưới dạng kiểu có thể truy vấn thì nhà cung cấp LINQ phải thể hiện nó như vậy. Ví dụ, LINQ to XML tải một tài liệu XML vào một kiểu XElement có thể truy vấn được  :

// Create a data source from an XML document.
// using System.Xml.Linq;
XElement contacts = XElement.Load(@"c:\myContactList.xml");

Với LINQ to SQL, trước tiên bạn tạo một ánh xạ quan hệ-đối tượng tại thời điểm thiết kế theo cách thủ công hoặc bằng cách sử dụng Công cụ LINQ to SQL trong Visual Studio. Bạn viết các truy vấn của mình đối với các đối tượng và tại thời điểm chạy LINQ to SQL sẽ xử lý việc giao tiếp với cơ sở dữ liệu. Trong ví dụ sau, Customers biểu thị một bảng cụ thể trong cơ sở dữ liệu và loại kết quả truy vấn, IQueryable<T>, xuất phát từ IEnumerable<T>.

Northwnd db = new Northwnd(@"c:\northwnd.mdf");

// Query for customers in London.
IQueryable<Customer> custQuery =
    from cust in db.Customers
    where cust.City == "London"
    select cust;

Để biết thêm thông tin về cách tạo các loại nguồn dữ liệu cụ thể, hãy xem tài liệu dành cho các nhà cung cấp LINQ khác nhau. Tuy nhiên, quy tắc cơ bản rất đơn giản: nguồn dữ liệu LINQ là bất kỳ đối tượng nào hỗ trợ giao diện IEnumerable<T> generic hoặc giao diện kế thừa từ nó.

Ghi chú

Các kiểu như ArrayList hỗ trợ interface IEnumerable non-generic cũng có thể được sử dụng làm nguồn dữ liệu LINQ. Để biết thêm thông tin, hãy xem Cách truy vấn ArrayList bằng LINQ (C#).

Truy vấn

Truy vấn chỉ định thông tin nào cần lấy từ nguồn dữ liệu hoặc các nguồn. Tùy chọn, một truy vấn cũng chỉ định cách sắp xếp, nhóm và định hình thông tin đó trước khi nó được trả về. Một truy vấn được lưu trữ trong một biến truy vấn và được khởi tạo bằng một biểu thức truy vấn. Để giúp việc viết truy vấn dễ dàng hơn, C# đã giới thiệu cú pháp truy vấn mới.

Truy vấn trong ví dụ trên trả về tất cả các số chẵn từ mảng số nguyên. Biểu thức truy vấn chứa ba mệnh đề: fromwhere và select. (Nếu bạn quen thuộc với SQL, bạn sẽ nhận thấy rằng thứ tự của các mệnh đề bị đảo ngược so với thứ tự trong SQL.) Mệnh đề from chỉ định nguồn dữ liệu, mệnh đề where áp dụng bộ lọc và mệnh đề select chỉ định kiểu phần tử được trả về. Các mệnh đề truy vấn này và các mệnh đề truy vấn khác sẽ được thảo luận chi tiết trong phần Truy vấn tích hợp ngôn ngữ (LINQ). Hiện tại, điểm quan trọng là trong LINQ, bản thân biến truy vấn không thực hiện hành động nào và không trả về dữ liệu. Nó chỉ lưu trữ thông tin cần thiết để tạo ra kết quả khi truy vấn được thực hiện ở một thời điểm nào đó sau đó. Để biết thêm thông tin về cách các truy vấn được xây dựng ở hậu trường, hãy xem Tổng quan về toán tử truy vấn tiêu chuẩn (C#).

Ghi chú

Các truy vấn cũng có thể được thể hiện bằng cách sử dụng cú pháp phương thức. Để biết thêm thông tin, hãy xem Cú pháp truy vấn và Cú pháp phương thức trong LINQ.

Thực thi truy vấn

Trì hoãn thực thi

Như đã nêu ở trên, bản thân biến truy vấn chỉ lưu trữ các lệnh truy vấn. Việc thực hiện truy vấn thực tế được hoãn lại cho đến khi bạn lặp lại biến truy vấn trong một câu lệnh foreach. Khái niệm này được gọi là thực thi hoãn lại và được thể hiện trong ví dụ sau:

//  Query execution.
foreach (int num in numQuery)
{
    Console.Write("{0,1} ", num);
}

Câu lệnh foreach cũng là nơi lấy ra kết quả truy vấn. Ví dụ: trong truy vấn trên, biến lặp num giữ từng giá trị (mỗi lần một giá trị) trong chuỗi được trả về.

Vì bản thân biến truy vấn không bao giờ chứa kết quả truy vấn nên bạn có thể thực hiện nó bao nhiêu lần tùy thích. Ví dụ: bạn có thể có cơ sở dữ liệu đang được cập nhật liên tục bởi một ứng dụng riêng biệt. Trong ứng dụng của mình, bạn có thể tạo một truy vấn để truy xuất dữ liệu mới nhất và bạn có thể thực hiện truy vấn đó nhiều lần trong một khoảng thời gian nào đó để truy xuất các kết quả khác nhau mỗi lần.

Buộc thực hiện ngay lập tức

Các truy vấn thực hiện các chức năng tổng hợp trên một phạm vi các phần tử nguồn trước tiên phải lặp lại các phần tử đó. Ví dụ về các truy vấn như vậy là CountMaxAverage và First. Chúng thực thi mà không có foreach câu lệnh rõ ràng vì bản thân truy vấn phải sử dụng foreach để trả về kết quả. Cũng lưu ý rằng các loại truy vấn này trả về một giá trị duy nhất, không phải một collection IEnumerable. Truy vấn sau đây trả về số lượng số chẵn trong mảng nguồn:

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

int evenNumCount = evenNumQuery.Count();

Để buộc thực hiện ngay lập tức bất kỳ truy vấn nào và lưu vào bộ đệm kết quả của nó, bạn có thể gọi các phương thức ToList hoặc ToArray.

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

// or like this:
// numQuery3 is still an int[]

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

Bạn cũng có thể buộc thực thi bằng cách đặt vòng lặp foreach ngay sau biểu thức truy vấn. Tuy nhiên, bằng cách gọi ToList hoặc ToArray bạn cũng lưu trữ tất cả dữ liệu trong một đối tượng bộ sưu tập.

Xem thêm

» Tiếp: Biểu thức nameof
« Trước: Tổng quan Ngôn ngữ tích hợp truy vấn (LINQ)
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!