LINQ

LINQ - Language Integrated Query (tạm dịch là ngôn ngữ truy vấn tích hợp) - là cách thức truy vấn dữ liệu từ một tập hợp dữ liệu. Hôm nay, chúng ta sẽ cùng tìm hiểu về LINQ và cách thức hoạt động của LINQ nhé.

Bạn đang xem: Linq

Nói đến lập trình, chúng ta không thể không nhắc đến dữ liệu. Dữ liệu ở đây có thể được bắt nguồn từ bộ nhớ, XML, cơ sở dữ liệu (Mysql, Sql Server...)... hay bất cứ nơi lưu trữ nào khác. Bất cứ dữ liệu đó bắt nguồn từ đâu, chúng ta phải luôn luôn thực hiện việc truy vấn dữ liệu đến nó. Với mỗi nguồn dữ liệu khác nhau sẽ có những cách khác nhau để thực hiện truy vấn.Ví dụ:- Nếu chúng ta có một danh sách được lưu trong bộ nhớ, chúng ta có thể sử dụng for, foreach kết hợp với các biểu thức so sánh để truy vấn dữ liệu.- Nếu chúng ta có dữ liệu trong file XML, chúng ta phải truy vấn từng node để có thể đưa ra các bản ghi hợp lệ.- Nếu chúng ta có dữ liệu trong cơ sở dữ liệu, chúng ta phải thực hiện truy vấn trong bảng rồi trả về các bản ghi hợp lệ.- ...Có lẽ đây là lý do mà các kỹ sư Microsoft đã thực hiện hợp nhất cách truy vấn thành 1 để có thể dùng chung cho các nguồn dữ liệu khác nhau. LINQ ra đời và được thêm vào phiên bản .NET 3.5.

Thành phần cấu thành LINQ

Nguồn dữ liệu.Tạo câu lệnh truy vấn.Thực thi truy vấn để lấy kết quả.

1. Nguồn dữ liệu

LINQ cung cấp cho chúng ta những providers khác nhau để có thể truy vấn đến những nguồn dữ liệu khác nhau. Dưới đây là bảng providers và giải thích:

Tên provider Mô tả
LINQ to Objects Sử dụng LINQ đối với các đối tượng collection mà implement từ IEnumerable hoặcIEnumerable (dữ liệu được lưu trong bộ nhớ). Được sử dụng rộng rãi đặc biệt đối với những bài toán cần hiệu năng cao.
LINQ to SQL Thực hiện map các tables, views, store procedures thành các đối tượng. LINQ sẽ thực hiện truy vấn trên các đối tượng đó bằng cách chuyển đổi qua lại giữa đối tượng và câu lệnh sql.Ngoài truy vấn ta cũng có thể thực hiện thêm/sửa/xóa dữ liệu dựa trên các đối tượng trên.Hỗ trợ transaction.Ưu điểm: được sử dụng khá nhiều trong thực tế dưới cái tên Entity Framework hoặc Entity Framework Core.Nhược điểm: chỉ làm việc với cơ sở dữ liệu là SQL Server.
LINQ to Entities Tương tự như LINQ to SQL nhưng hỗ trợ nhiều loại cơ sở dữ liệu.Nhược điểm: sử dụng phức tạp. Nhiều cơ sở dữ liệu không thích hợp sử dụng chung với .Net.

Xem thêm: Thi 2016: Điều Chỉnh Quy Trình Xét Tuyển Đại Học 2016 Và Xét Tuyển Đh

LINQ to DataSets Sự kết hợp giữa LINQ và ADO.NET.
LINQ toXML Truy vấn thông tin trong file XML.

2. Tạo câu lệnh truy vấn

Có 2 cách để tạo ra câu truy vấn: cú pháp truy vấn (query syntax) và cú pháp phương thức (method syntax). Và để viết được câu truy vấn bạn cần using thư viện System.Linq.Mình có đưa tạo ra class Car và danh sách các Car để làm ví dụ cho bài viết như sau:

public class Car { public int Id { get; set; } public string Name { get; set; } public int YearOfManufacture { get; set; } public override string ToString() { return string.Format("{0} - {1} - {2}", Id, Name, YearOfManufacture); } public static IList Cars { get { return new List { new Car { Id = 1, Name = "Honda", YearOfManufacture = 1990 }, new Car { Id = 2, Name = "Toyota", YearOfManufacture = 1880 }, new Car { Id = 3, Name = "Mazda", YearOfManufacture = 1925 }, new Car { Id = 4, Name = "VinFast", YearOfManufacture = 2017 }, new Car { Id = 5, Name = "KIA", YearOfManufacture = 1855 }, new Car { Id = 6, Name = "BMW", YearOfManufacture = 1946 }, new Car { Id = 7, Name = "Merc", YearOfManufacture = 1981 }, new Car { Id = 8, Name = "Abc", YearOfManufacture = 1920 }, new Car { Id = 9, Name = "Def", YearOfManufacture = 1940 }, new Car { Id = 10, Name = "Gij", YearOfManufacture = 1944 } }; } } }

2.1. Cú pháp truy vấn

Sử dụng cú pháp giống như bạn truy vấn cơ sở dữ liệu. Mẫu cú pháp sẽ như sau:

var lists = from in select Ví dụ sau sẽ hiển thị danh sách các xe có năm sản xuất >= 1990.

class Program { static void Main(string args) { var cars = Car.Cars; var list = from item in cars where item.YearOfManufacture >= 1990 select item; foreach (var item in list) { Console.WriteLine(item); } /* * Output: * * 1 - Honda - 1990 * 4 - VinFast - 2017 */ Console.ReadKey(); } }Các bạn có thể tham khảo các phép toán truy vấn tại đây.Với cách viết như này, chúng ta sẽ rất dễ tiếp cận vì nó gần giống với truy vấn cơ sở dữ liệu nhưng chúng ta sẽ không hiểu được bản chất của LINQ là gì nên tôi sẽ không đi quá chi tiết. Vì khi biên dịch, cú pháp truy vấn cũng đưa về cú pháp phương thức.

2.2. Cú pháp phương thức

Là những phương thức mở rộng của IEnumerable hoặc IEnumerable.Với ví dụ trên chúng ta thay đổi code như sau:

class Program { static void Main(string args) { var cars = Car.Cars; var list = cars.Where(f => f.YearOfManufacture >= 1990); foreach (var item in list) { Console.WriteLine(item); } /* * Output: * * 1 - Honda - 1990 * 4 - VinFast - 2017 */ Console.ReadKey(); } }Chúng ta để ý thấy rằng trong hàm Where xuất hiện tham số rất loằng ngoằng (f => f.YearOfManufacture >= 1990). Vậy tham số đó là gì vậy?Giờ chúng ta hãy xem signature của hàm Where:

public static IEnumerable Where(this IEnumerable source, Func predicate);Ohh, hóa ra nó là một biểu thức lambda mà mình đã đề cập chi tiết tại đây.Giờ chúng ta hãy thử viết một hàm LINQ mới như sau:

public static class CarExtenstion { public static IEnumerable MyQuery(this IEnumerable list, Func myCondition) { foreach (var item in list) { if (myCondition(item)) yield return item; } } }Chúng ta sử dụng hàm đó như sau:

class Program { static void Main(string args) { var cars = Car.Cars; var list = cars.MyQuery(f => f.YearOfManufacture >= 1990); foreach (var item in list) { Console.WriteLine(item); } /* * Output: * * 1 - Honda - 1990 * 4 - VinFast - 2017 */ Console.ReadKey(); } }Đến đây thì chúng ta đã hiểu LINQ sử dụng trong C# như thế nào và cách viết các hàm LINQ. Cuối cùng cũng là sự kết hợp giữa phương thức mở rộng, lambda expression và yield return.

3. Thực thi truy vấn để lấy kết quả

Có 2 kịch bản để thực thi truy vấn:- Trì hoãn thực thi (Deferred Execution): việc thực thi truy vấn chỉ khi thực hiện foreach như hình dưới đây

Sử dụng cách thực thi này sẽ giúp:+ Cải thiện hiệu năng vì chỉ thực thi một lần.+ Dữ liệu trả về là mới nhất.- Thực thi ngay lập tức (Immediate Execution): thực thi ngay lập tức câu lệnh và trả về kết quả. Để thực thi ngay lập tức, chúng ta có thể dùng những toán tử chuyển đổi như ToList, ToArray, ToDictionary... hoặc những toán tử thành phần như First, FirstOrDefault, Last, LastOrDefault...Với ví dụ trên, chúng ta sửa code lại như sau để thực thi câu lệnh ngay lập tức:

class Program { static void Main(string args) { var cars = Car.Cars; var list = cars.Where(f => f.YearOfManufacture >= 1990).ToList(); foreach (var item in list) { Console.WriteLine(item); } /* * Output: * * 1 - Honda - 1990 * 4 - VinFast - 2017 */ Console.ReadKey(); } }

4. Lưu ý

Sau khi tìm hiểu đến đây, mình đoán có nhiều bạn sẽ hỏi: Tại sao không sử dụng for hoặc foreach kết hợp if để truy vấn? Hay LINQ hiệu năng như thế nào? Theo quan điểm cá nhân của mình thì việc sử dụng hay không sử dụng LINQ còn tùy thuộc vào quan điểm của mỗi người. Mình thích dùng vì thấy dễ viết, ngắn gọn và dễ debug. Về hiệu năng thì mặc dù LINQ có chậm hơn so với việc sử dụng for hoặc foreach nhưng với việc cấu hình máy tính, máy chủ khủng như hiện nay thì khoảng chậm đó cũng không ảnh hưởng lắm đến hiệu năng.Dưới đây mình có làm một phép so sánh giữa việc sử dụng for, foreach, LINQ các bạn hãy tham khảo nhé:

class Program { static void Main(string args) { var text = "531d"; var listGuid = RandomList(); var stopWatch = new Stopwatch(); stopWatch.Start(); var count = FindByFor(text, listGuid); stopWatch.Stop(); var ms = stopWatch.Elapsed.TotalMilliseconds; Console.WriteLine("For Execution Time: " + ms + " (ms). Count: " + count + " (items)"); stopWatch.Restart(); count = FindByForeach(text, listGuid); stopWatch.Stop(); ms = stopWatch.Elapsed.TotalMilliseconds; Console.WriteLine("Foreach Execution Time: " + ms + " (ms). Count: " + count + " (items)"); stopWatch.Restart(); count = FindByLinq(text, listGuid); stopWatch.Stop(); ms = stopWatch.Elapsed.TotalMilliseconds; Console.WriteLine("Linq Execution Time: " + ms + " (ms). Count: " + count + " (items)"); Console.ReadKey(); } private static int FindByFor(string text, IList listGuid) { var length = listGuid.Count; var count = 0; for (int i = 0; i listGuid) { var count = 0; foreach (var item in listGuid) { if (!item.Contains(text)) continue; count++; } return count; } private static int FindByLinq(string text, IList listGuid) { var list = listGuid.Where(f => f.Contains(text)); return list.Count(); } static IList RandomList() { var d = new List(); for (int i = 0; i Và kết quả của một số lần chạy thử như sau:Chỉ hơn kém nhau có 6 (ms).

Kết luận:

Mình đã chia sẻ những hiểu biết của mình về LINQ, rất mong nó sẽ có ích cho các bạn. Nếu có thắc mắc gì, hãy để lại comment bên dưới nhé.