Một khi đã làm quen với
Lambda Expression, bạn có thể dễ dàng tiếp cận và sử dụng LINQ (Language-Integrated Query) để thao tác với các collection. Ra đời từ phiên bản .Net 3.5 gắn liền với
Extension Method và
các chức năng lập trình mới, LINQ đã trở thành “cầu nối để xóa đi khoảng cách giữa hai thế giới: object và data”.
Giới thiệu
LINQ được chia thành ba loại chính dựa vào đối tượng sử dụng:
- LINQ to Objects
- LINQ to XML
- LINQ-enabled ADO.NET (LINQ to SQL, LINQ to DataSet và LINQ to Entities)
Kiến trúc của LINQ được minh họa như sau (ảnh từ MSDN Magazine):
Bạn có thể tham khảo trang chủ của
LINQ Project trên MSDN để xem thêm thông tin và hướng dẫn.
“The .NET Standard Query Operators”
Đây là tiêu đề của cuốn tài liệu được cung cấp trên trang LINQ Project (
download) được cung cấp để giới thiệu các toán tử truy vấn trong LINQ. Theo đó các toán tử này là một API cho phép bạn có thể thực hiện truy vấn với bất kì mảng hoặc collection nào của .Net.
Do LINQ hỗ trợ hai dạng cú pháp là dùng câu truy vấn (query) và gọi phương thức nên mặc dù bản chất
Query Operators là các extension method nhưng lại được coi là các toán tử (operator).
Chúng được chia thành các loại sau:
Category | Operators |
Restriction | Where |
Projection | Select, SelectMany |
Partitioning | Take, Skip, TakeWhile, SkipWhile |
Join | Join, GroupJoin |
Concatenation | Concat |
Ordering | OrderBy / ThenBy, Reverse |
Grouping | GroupBy |
Set | Distinct, Union, Intersect, Except |
Conversion | ToSequence, ToArray, ToList, ToDictionary, ToLookup, OfType, Cast |
Equality | EqualAll |
Element | First, FirstOrDefault, Last, LastOrDefault, Single, SingleOrDefault, ElementAt, ElementAtOrDefault, DefaultIfEmpty |
Generation | Range, Repeat, Empty |
Quantifiers | Any, All, Contains |
Aggregate | Count, LongCount, Sum, Min, Max, Average, Aggregate |
Việc giới thiệu và liệt kê hết các toán tử của LINQ trong một bài viết là một công việc không cần thiết, thay vào đó, bạn có thể vào trang
101 LINQ Samples cùng với tài liệu này để xem hướng dẫn và ví dụ cách sử dụng các toán tử này.
Từ Lambda Expression đến LINQ: Method Syntax vs Query Syntax
Để bắt đầu với làm quen với cú pháp truy vấn của LINQ, trước tiên bạn hãy thêm namespace System.Linq vào lớp cần sử dụng. Ta sẽ làm việc với hai mảng dữ liệu sau để minh họa cho các ví dụ sử dụng một vài toán tử truy vấn để so sánh giữa Method syntax và Query syntax:
string[] names=new string[]{
"Olivia",
"Ruby",
"Lily",
"Sophie",
"Emily",
"Chloe",
"Grace",
"Jessica",
"Evie",
"Amelia"};
int[] ages=new int[]{ 12, 34, 6, 78, 32, 9, 16, 52, 28, 10 };
Bạn sẽ không khó để hiểu cú pháp của câu query nếu như bạn hiểu được lambda expression, các tham số và method/operator của chúng tương ứng nhau. Và bởi vì cú pháp của câu dạng truy vấn chỉ hỗ trợ một vài từ khóa đơn giản (from, in, select, where, group,… ) nên một số câu truy vấn bạn cần kết hợp cả hai loại cú pháp để đạt được mục đích thay vì chia nhỏ chúng ra. Cú pháp “hỗn hợp” này có thể được gọi với tên Query Dot syntax.
- Where operator:
Toán tử Where dùng để lọc dữ liệu dựa vào điều kiện mà bạn đưa ra.
Ta có ví dụ sử dụng Where để lấy ra các tên bắt đầu bằng “E”. Với Method Syntax sử dụng Lambda Expression, ta thấy rất đơn giản:
// #1: method syntax (lambda expression)
IEnumerable<string> x = names.Where(n => n.StartsWith("E"));
Và với query syntax:
// #2: query syntax
IEnumerable<string> y =
from n
in names
where n.StartsWith("E")
select n;
Kết quả xuất ra cho x và y là như nhau:
foreach(var item in y)
Console.WriteLine(item);
Ouput:
Emily
Evie
Thay vì sử dụng kiểu trả về là IEnumerble<T>, sẽ tiện hơn nếu ta sử dụng var để không cần xác định chính xác kiểu trả về khi viết mã lệnh, như các ví dụ phía sau đây.
- Select operator
Toán tử Select dùng để lấy/trích ra dữ liệu từ mỗi phần tử trong tập dữ liệu.
Ví dụ: Lấy ra chiều dài của tên trong mảng names. Hai câu lệnh sau đều trả về một đối tượng có kiểu IEnumerable.
// #1: method syntax (lambda expression)
var x = names.Select(n => n.Length);
// #2: query syntax
var y =
from n
in names
select n.Length;
/* Output:
6
4
4
6
5
5
5
7
4
6
*/
- Take operator
Toán tử này sẽ lấy N phần tử đầu tiên trong một tập dữ liệu
Ví dụ: Lấy 5 phần tử đầu:
// #1: method syntax (lambda expression)
var x = names.Take(5);
// #2: query syntax
var y =
(from n
in names
select n).Take(5) ;
/* Output:
Olivia
Ruby
Lily
Sophie
Emily
*/
- Concat operator
Toán tử này sẽ nối hai tập dữ liệu với nhau, hai tập dữ liệu này phải có cùng kiểu:
Ví dụ sau sẽ nối mảng names và mảng ages lại thành một IEnumerablegồm 20 phần tử:
// #1: method syntax (lambda expression)
var x = names.Concat(ages.Select(a => a.ToString()));
// #2: query syntax
var y =
names.Concat(
from a
in ages
select a.ToString());
- OrderBy/ThenBy operator
Dùng để sắp xếp theo một hay nhiều thuộc tính của các phần tử, mặc định là xếp tăng dần. Bạn có thể thêm từ khóa descending phía sau để xếp theo chiều ngược lại.
Để xếp theo nhiều thuộc tính:
+ Method syntax: Sử dụng thêm ThenBy, ThenByDescending
+ Query syntax: Các thuộc tính cần xếp cách nhau bởi dấu phẩy
Ví dụ: Xếp mảng names theo độ dài giảm dần, nếu các tên có cùng độ dài sẽ xếp theo thứ tự alphabe:
// #1: method syntax (lambda expression)
var x = names.<strong>OrderByDescending</strong>(n => n.Length).<strong>ThenBy</strong>(n => n);
// #2: query syntax
var y =
from n
<strong>in</strong> names
orderby n.Length descending,n
select n;
/* Output:
Jessica
Amelia
Olivia
Sophie
Chloe
Emily
Grace
Evie
Lily
Ruby
*/
Phần kết
Qua các ví dụ trên có lẽ bạn cũng phần nào hiểu được cú pháp sử dụng câu truy vấn LINQ. Các toán tử LINQ khác bạn có thể nhanh chóng hiểu được thông qua các tài liệu mà tôi đã đề cập trong bài viết. Cuối cùng bài viết này chỉ có mục đích chính là giúp bạn hiểu được cú pháp sử dụng LINQ theo cả hai cách: Method Syntax và Query Syntax. Ta chưa bàn đến những đặc điểm của các toán tử cũng như cách thức mà chúng hoạt động. Bạn có thể tìm thấy những vấn đề đó ở một bài viết khác.