CodeGym /Các khóa học /C# SELF /Giới thiệu về LINQ và lợi ích của nó

Giới thiệu về LINQ và lợi ích của nó

C# SELF
Mức độ, Bài học
Có sẵn

1. Giới thiệu

Hãy tưởng tượng bạn đến thư viện và cần tìm tất cả sách về lập trình xuất bản sau năm 2020, được sắp xếp theo tên tác giả. Chắc chắn bạn sẽ không chạy từ kệ này sang kệ khác và kiểm tra từng cuốn sách một cách thủ công đúng không? Bạn sẽ nhờ thủ thư, người biết cách tìm nhanh thứ bạn cần.

LINQ (Language Integrated Query, hay "Truy vấn tích hợp ngôn ngữ") – chính là "thủ thư thông minh" của chúng ta, "SQL-động cơ" (tức là công cụ để viết truy vấn dữ liệu) ngay trong C#!

Hãy nghĩ về LINQ như một ngôn ngữ cho phép bạn mô tả bạn muốn lấy gì từ dữ liệu, chứ không phải làm thế nào để lấy nó. Thay vì phải yêu cầu: "Lấy phần tử đầu tiên, kiểm tra điều kiện, nếu đúng thì thêm vào list mới, sau đó chuyển sang phần tử thứ hai...", bạn chỉ cần nói: "Đưa tôi tất cả sản phẩm có giá lớn hơn 1000". Và C# sẽ tự biết cách thực hiện hiệu quả nhất.

Ý tưởng chính của LINQ: cung cấp cú pháp chuẩn cho truy vấn bất kỳ nguồn dữ liệu nào mà implement interface IEnumerable<T>. Và điều này thật tuyệt, vì List<T>, array, HashSet<T> – tất cả đều implement IEnumerable<T>. Nếu dữ liệu của bạn nằm trong database, thì các thư viện đặc biệt (ví dụ Entity Framework) sẽ chuyển LINQ-query của bạn thành SQL thực sự!

LINQ xuất hiện trong C# hơn 10 năm trước. Đó là một sự kiện quan trọng đã thay đổi cách làm việc với dữ liệu trong .NET. Trước LINQ, dev phải viết rất nhiều code mẫu để lọc, sắp xếp và chuyển đổi collection. Hoặc dùng string SQL-query, mà compiler không kiểm tra và dễ bị lỗi runtime. LINQ mang khái niệm "truy vấn" vào tận ngôn ngữ lập trình, làm cho chúng type-safe và dễ đọc hơn nhiều.

Nhiều người cho rằng LINQ là một trong những cải tiến lớn nhất của C# kể từ khi có Generics.

2. Lợi ích của LINQ: Tại sao ai cũng thích?

Ngắn gọn và dễ đọc: Để lọc, ví dụ, sản phẩm có giá lớn hơn 1000 trong một database lớn, trước đây phải viết vài dòng code. Với LINQ, chỉ cần một dòng. Code ít hơn — ít bug hơn, dễ đọc và dễ hiểu hơn. Code gần với ngôn ngữ tự nhiên hơn.
Joke lập trình viên: "Càng viết ít code, mình càng nhét được ít bug vào đó." LINQ giúp bạn điều này!

Mạnh mẽ và linh hoạt: LINQ cung cấp rất nhiều operation: lọc (Where), chuyển đổi (Select), sắp xếp (OrderBy), nhóm (GroupBy), tổng hợp (Sum, Average), join (Join) và nhiều nữa. Bạn có thể giải quyết các bài toán xử lý dữ liệu phức tạp bằng cách kết hợp các operation này.

Type-safe: Cái này quan trọng lắm! Khi bạn viết SQL-query dưới dạng string, compiler không biết gì về nó. Nếu bạn sai tên cột, chỉ khi chạy chương trình mới biết lỗi. Với LINQ, compiler C# kiểm tra query của bạn ngay lúc compile. Nếu bạn truy vấn field không tồn tại trong object Product, compiler sẽ báo lỗi ngay. Kiểu như có một "checker" cá nhân bắt lỗi chính tả của bạn trước khi người khác thấy.

Tích hợp với ngôn ngữ: LINQ-query không phải là string "ma thuật" nào đó. Nó là construct C# thực thụ, dùng lambda expression quen thuộc và làm việc với các kiểu dữ liệu bạn đã biết. Việc chuyển sang LINQ rất mượt mà.

Deferred Execution (Thực thi trì hoãn): Đây là một trong những concept ngầu nhất và cũng khó nhất của LINQ, nhưng rất quan trọng. Ý là LINQ-query không chạy ngay khi bạn viết nó. Nó "được xây dựng" và chờ đến khi bạn thực sự cần kết quả (ví dụ, bắt đầu duyệt nó bằng foreach). Điều này giúp tối ưu hóa query, nhất là khi làm việc với dữ liệu lớn. Chúng ta sẽ nói kỹ hơn ở Bài giảng 166. Giờ chỉ cần nhớ: LINQ thông minh, nó không làm việc thừa.

Đa năng và mở rộng: LINQ không chỉ cho list trong memory. Có nhiều "provider" LINQ khác nhau:

  • LINQ to Objects: cho collection trong memory (List<T>, array, v.v.).
  • LINQ to SQL / Entity Framework Core: cho SQL database (LINQ-query của bạn được chuyển thành SQL-query).
  • LINQ to XML: để làm việc với XML-document.
  • Và còn nhiều nữa.
    Nghĩa là, khi đã quen LINQ, bạn có thể làm việc với dữ liệu từ nhiều nguồn khác nhau, dùng cùng một logic truy vấn.

3. Vấn đề khi làm việc với collection "kiểu cũ"

Trước khi có LINQ, code xử lý collection trong C# thường như này:


var products = new List<Product> { /* ... */ };

var expensive = new List<Product>();
foreach (var prod in products)
{
    if (prod.Price > 100)
        expensive.Add(prod);
}

expensive.Sort((a, b) => a.Price.CompareTo(b.Price));

foreach (var item in expensive)
    Console.WriteLine(item.Name);

Kiểu này rất phổ biến: đầu tiên lọc thủ công, rồi sort — phải tạo list trung gian. Logic càng nhiều, code càng dài, càng dễ lỗi và nhiều biến phụ. Tất cả giống như xưởng lắp ráp đồ gỗ: có đủ chi tiết, nhưng lắp ráp thì thủ công và mệt mỏi.

Alice ở xứ sở vòng lặp

Hãy tưởng tượng bạn có một list user rất lớn, và bạn muốn lấy tên những người trên 18 tuổi và sống ở thành phố Neonvil, sắp xếp theo alphabet và lấy ba người đầu tiên. Bạn sẽ phải viết vòng lặp lồng nhau, điều kiện, sort, list phụ... hoặc chỉ cần dùng LINQ.

4. LINQ-query trông như thế nào? Ví dụ đơn giản

LINQ có hai cú pháp chính:

  • Method Syntax (chuỗi method)
  • Query Syntax (ngôn ngữ giống SQL)

Thường thì dùng Method Syntax nhiều hơn, nhất là trong project hiện đại. Đây là ví dụ dựa trên app của chúng ta:


var expensive = products
    .Where(p => p.Price > 100)
    .OrderBy(p => p.Price)
    .Select(p => p.Name)
    .Take(3);

foreach (var name in expensive)
    Console.WriteLine(name);

Rất rõ ràng: lọc, sort, chọn field cụ thể, giới hạn số lượng. Code tối thiểu — ý nghĩa tối đa.

Cùng ví dụ với Query Syntax


var expensive = from p in products
                where p.Price > 100
                orderby p.Price
                select p.Name;

foreach (var name in expensive.Take(3))
    Console.WriteLine(name);

Cả hai cách đều cho kết quả như nhau — chọn cách bạn thích (nhưng method-syntax vẫn phổ biến hơn).

5. Kiến trúc LINQ: Bên trong có gì?

LINQ làm việc với bất kỳ collection nào implement interface IEnumerable<T> (hoặc IQueryable<T>, sẽ nói sau).

Thành phần chính của LINQ

Thành phần Mô tả ngắn
Class mở rộng LINQ Static method (
Where
,
Select
,
OrderBy
v.v.) trong class
System.Linq.Enumerable
Delegate Thường dùng nhất là
Func<T, TResult>
và
Predicate<T>
Deferred execution Query chỉ được tính toán khi duyệt lần đầu (ví dụ qua
foreach
)
Query Provider (cho LINQ to SQL, LINQ to Entities) — chuyển chuỗi method thành SQL-query v.v.

Sơ đồ trực quan


Collection (List<Product>, T[], ...) 
      │
      ▼
LINQ-method (Where, OrderBy, Select...)
      │
      ▼
Query (IEnumerable<T>) 
      │
      ▼
Thực thi thực tế (foreach, ToList, Count, ...)

6. Ví dụ: Phân tích từng bước LINQ-chain trong app của chúng ta

Quay lại mini-app với class Product:


// Class sản phẩm
class Product
{
    public string Name { get; set; }
    public double Price { get; set; }
}

Đây là danh sách sản phẩm:


var products = new List<Product>
{
    new Product { Name = "Phô mai", Price = 250.5 },
    new Product { Name = "Bánh mì", Price = 30 },
    new Product { Name = "Sữa", Price = 80 },
    new Product { Name = "Cà phê", Price = 330 },
    new Product { Name = "Bơ", Price = 140 }
};

Giả sử nhiệm vụ là: in ra màn hình tên tất cả sản phẩm đắt hơn 100 euro, sắp xếp theo giá.

Cách cũ


var filtered = new List<Product>();
foreach (var p in products)
{
    if (p.Price > 100)
        filtered.Add(p);
}

filtered.Sort((a, b) => a.Price.CompareTo(b.Price));

foreach (var p in filtered)
    Console.WriteLine(p.Name);

Cách LINQ


var expensive = products
    .Where(p => p.Price > 100)
    .OrderBy(p => p.Price)
    .Select(p => p.Name);

foreach (var name in expensive)
    Console.WriteLine(name);

Ngay dòng đầu tiên, ta mô tả đúng bản chất bài toán: "Lọc những ai có Price > 100, sắp xếp theo giá, lấy tên".

7. Các thao tác LINQ thường gặp và cách làm "kiểu cũ"

Thao tác Logic "kiểu cũ" LINQ
Lọc foreach + if + Add
Where
Chuyển đổi (chọn field) foreach + Add(field)
Select
Sắp xếp Sort(comparer)
OrderBy
,
OrderByDescending
Giá trị duy nhất foreach + contains + Add
Distinct
Đếm foreach + counter++
Count
,
Count(predicate)
Kiểm tra điều kiện foreach + if
Any
,
All
Tìm phần tử đầu/cuối foreach + if + break
First
,
Last
,
FirstOrDefault
Giới hạn số lượng foreach + counter + break
Take
,
Skip

8. Thực hành: Thêm LINQ vào app

Lấy code cơ bản của app (Product-list) và thử làm vài thao tác hữu ích với LINQ.

Lọc và sắp xếp


// Chọn sản phẩm rẻ hơn 200 euro và sắp xếp theo tên
var cheapProducts = products
    .Where(p => p.Price < 200)
    .OrderBy(p => p.Name);

foreach (var p in cheapProducts)
    Console.WriteLine($"{p.Name}: {p.Price} euro");

Chuyển đổi (projection) collection


// Lấy list giá (double)
var prices = products.Select(p => p.Price);

foreach (var price in prices)
    Console.WriteLine(price);

Tìm phần tử đầu tiên phù hợp


// Sản phẩm đầu tiên có tên bắt đầu bằng "C"
var firstK = products.FirstOrDefault(p => p.Name.StartsWith("C"));
Console.WriteLine(firstK?.Name ?? "Không tìm thấy");

Đếm số sản phẩm đắt hơn 100 euro


int count = products.Count(p => p.Price > 100);
Console.WriteLine($"Có {count} sản phẩm như vậy.");

Từ bài này trở đi, chúng ta sẽ dùng LINQ thường xuyên để xử lý collection, lọc, chọn dữ liệu cần thiết, tổng hợp và nhiều thứ khác. Các method mới và tính năng LINQ (cả những thứ mới của .NET 9!) sẽ được phân tích ở các bài tiếp theo, còn bây giờ — cứ mạnh dạn thử nghiệm với các query đơn giản để cảm nhận sức mạnh của công cụ này nhé!

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Lọc các số từ mảng
Lọc các số từ mảng
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION