Vài tuần trước tôi bắt đầu viết loạt bài về LINQ to SQL. LINQ to SQL là một bộ khung (framework) có sẵn cho O/RM (object relational mapping) trong .NET 3.5, nó cho phép bạn dễ dàng mô hình hóa các CSDL quan hệ dùng các lớp .NET. Bạn có thể dùng các biểu thức LINQ để truy vấn CSDL, cũng như có thể cập nhật/thêm/xóa dữ liệu từ đó.
Dưới đây là 7 phần đầu tiên của loạt bài này:
- Sử dụng LINQ to SQL (phần 1)
- Định nghĩa các lớp mô hình dữ liệu (phần 2)
- Truy vấn Cơ sở dữ liệu (phần 3)
- Cập nhật cơ sở dữ liệu (LINQ to SQL phần 4)
- Sử dụng asp:LinqDataSource (phần 5)
- Lấy dữ liệu dùng Stored Procedure (LINQ to SQL phần 6)
- Cập nhật dữ liệu dùng Stored Procedure (LINQ to SQL phần 7)
Trong hai bài cuối (bài 6 và bài 7), tôi đã biểu diễn cách bạn có thể dùng các thủ tục trong CSDL (SPROC) để thực hiện truy vấn, cập nhật, thêm hoặc xóa dữ liệu dùng mô hình dữ liệu LINQ to SQL.
Có một vài bạn đã hỏi tôi khi viết các bài này là “Liệu tôi có thể kiểm soát hoàn toàn các câu SQL được dùng bởi LINQ to SQL mà không cần phải viết các SPROC?”. Trong bài viết này tôi sẽ nói về điều này – và thảo luận cách bạn có thể viết các câu SQL tùy biến để truy vấn, cũng như để thêm, sửa hay xóa dữ liệu.
Dùng các biểu thức truy vấn LINQ với LINQ to SQL
Trong bài viết này, chúng ta sẽ dùng mô hình mô hình dữ liệu được tạo với CSDL Northwind (xin hãy đọc phần 2 để học cách dùng VS 2008 để tạo ra mô hình này):
Trong phần 3, tôi đã cho các bạn thấy cách dùng ngôn ngữ LINQ mới được đưa vào VB và C# để truy vấn mô hình dữ liệu ở trên và trả về một tập đối tượng biểu diễn các dòng/cột trong CSDL.
Ví dụ, bạn có thể thêm một phương thức trợ giúp “GetProductsByCategory” vào lớp DataContext trong mô hình dữ liệu của chúng ta mà nó sẽ dùng một các truy vấn LINQ để trả về các đối tượng Product từ CSDL:
VB:
C#:
Một khi bạn đã định nghĩa phương thức LINQ như trên, bạn có thể viết lệnh giống như dưới đây để dùng nó lấy về các sản phẩm, và duyệt qua tập kết quả trả về:
VB:
Khi biểu thức LINQ bên trong phương thức “GetProductsByCategory” được thực thi, trình quản ly LINQ to SQL sẽ tự động thực thi câu SQL động để lấy về dữ liệu Product và tạo ra danh sách các đối tượng Product. Bạn có thể dùng trình debug để xem cách biểu thức LINQ này thực thi.
Dùng các câu truy vấn SQL tùy biến với LINQ to SQL
Trong ví dụ mẫu ở trên chúng ta đã không viết bất kỳ câu lệnh SQL nào để truy vấn dữ liệu và lấy về các đống tượng có kiểu Product. Thay vì vậy, LINQ to SQL sẽ tự đọng dịch biểu thức LINQ thành câu lệnh SQL chúng ta và thực thi nó trong CSDL.
Nhưng liệu nếu chúng ta muốn kiểm soát hoàn toàn câu lệnh SQL được thực thi với CSDL, và không muôn LINQ to SQL làm điều đó tự động? Một cách để làm điều này là dùng một SPROC giống như tôi đã trình bày trong bài 6 và bài 7. Một cách khác là dùng phương thức “ExcecuteQuery” trong lớp DataContext để thực thi một câu SQL do chúng ta cung cấp.
Dùng ExecuteQuery
Phương thức ExecuteQuery nhận vào một câu SQL, cùng với một tập các tham số mà ta có thể dùng để tạo nên câu SQL. Bằng cách dùng nó, bạn có thể thực thi bất kỳ câu lệnh SQL bạn muốn với CSDL (kể các câu lệnh JOIN nhiều bảng).
Điều làm cho ExecuteQuery thực sự hữu dụng là nó cho phép bạn chỉ ra cách nó trả về dữ liệu. Bạn có thể làm được điều này bằng cách truyền một đối tượng có kiểu mong muốn như một tham số của phương thức, hay dùng kiểu generic.
Ví dụ, bạn có thể thay đổi phương thức GetProductsByCategory() được tạo ra trước đây – phiên bản dùng một biểu thức LINQ – để dùng phương thức ExecuteQuery thực thi một câu SQL với CSDL và trả về một tập đối tượng Product như kết quả:
VB:
C#:
Chúng ta có thể gọi GetProductsByCategory() dùng cùng cách như trước đây:
Nhưng không như trước đây, trong trường hợp này câu SQL tùy biến sẽ được gọi thay cho câu SQL động được tạo bởi biểu thức LINQ.
Tùy biến các biểu thức SQL và theo vết (tracking) các thao tác cập nhật:
Mặc nhiên, khi bạn lấy về một mô hình dữ liệu dùng LINQ to SQL, nó sẽ lưu lại các thay đổi mà bạn làm. Nếu gọi phương thức “SubmitChanges()” trên lớp DataContext, nó sẽ lưu lại các thay đổi vào CSDL. Tôi đã nói chi tiết về vấn đề này trong phần 4 của loạt bài này.
Một trong nhưng tính năng nổi trọi của ExecuteQuery là nó có thể kết hợp hoàn toàn vào quá trình theo vết và cập nhật lại mô hình dữ liệu. Ví dụ, bạn có thể viết đoạn lệnh dưới đây để lấy về tất cả các sản phẩm từ một chủng loại nào đó và giảm giá toàn bộ 10%:
Bởi vì chúng ta đã chỉ ra rõ kiểu trả về của câu lệnh ExecuteQuery trong phương thức GetProductsByCategory, do vậy LINQ to SQL sẽ biết cách để dò ra các thay đổi trên các đối tượng Product mà chúng ta trả về, và khi gọi “SubmitChanges()” trên đối tượng đó, chúng sẽ được lưu lại trong SCDL.
Tùy biến các biểu thức SQL với các lớp của bạn
Phương thức ExecuteQuery() cho phép bạn chỉ ra bất kỳ lớp nào như kiểu trả về của câu truy vấn. Lớp này không nhất thiết phải được tạo ra bởi trình LINQ to SQL designer, hay phải thừa kế từ bất kỳ class/interface nào.
Ví dụ, bạn có thể định nghĩa một lớp ProductSummary mới chứa các thuộc tính là tập con của Product như dưới đây (chú ý là chúng ta dùng đặc tính Automatic Properties mới có trong C#):
Chúng ta có thể sau đó tạo ra một phương thức tên là GetProductSummariesByCategory() trong lớp NorthwindDataContext, nó sẽ trả về các kết quả dựa trên kiểu ProductSummary. Để ý là câu SQL dưới đây chỉ yêu cầu các thuộc tính của Product nó cần – ExecuteQuery sẽ tự biết cách đưa các giá trị đó vào các đối tượng ProductSummary mà nó sẽ trả về.
Sau đó chúng ta có thể dùng phương thức này để truy vấn và duyệt qua tập kết quả trả về:
Tùy biến các câu SQL cho Inserts/Updates/Deletes
Thêm vào việc dùng các biểu thức SQL tùy biến để truy vấn, bạn cũng có thể dùng chúng để thực hiện các thao tác như thêm/xóa/sửa.
Chúng ta có thể làm được điều này bằng cách tạo ra các phương thức partial trong lớp DataContext tương ứng các thao tác Insert/Update/Delete cho thực thể mà chúng ta muốn thay đổi. Và chúng ta sau đó có thể dùng phương thức ExecuteCommand để thực thi các câu SQL cần thiết. Ví dụ, để thay thế hành vi Delete mặc nhiên cho lớp Product, bạn có thể định nghĩa một phương thức partial DeleteProduct như sau:
Và bây giờ, nếu bạn viết đọa code dưới đây để xóa một Product nào đó khỏi CSDL, LINQ to SQL sẽ gọi phương thức DeleteProduct – và khi đó các câu SQL tùy biến sẽ được thực thi thay thế cho câu SQL được sinh ra tự động bởi LINQ to SQL:
Tổng kết
Trình quản lý LINQ to SQL tự động tạo ra và thực thi các câu SQL động để thực hiện các câu truy vấn, cập nhật, thêm và xóa dữ liệu trong CSDL.
Đối với một số trường hợp, khi bạn muốn kiểm soát hoàn toàn câu lệnh SQL được thực thi, bạn có thể dùng các thủ tục SPROC, hay cũng có thể viết các câu SQL của riêng bạn. Điều này cung cấp khả năng tùy biến mạnh mẽ khi xây dựng các lớp truy cập dữ liệu.
Trong bài viết tiếp theo, tôi sẽ nói đến một số khái niệm còn lại như: Single Table Inheritance, Deferred/Eager Loading, Optimistic Concurrency, và sử dụng trong hô mình Multi-Tier.
Hope this helps,
Scott
ExecuteQuery proc trả về số lượng record của 1 table thì phải viết như thế nào hả a?
Em dùng ‘SELECT COUNT(*) FROM tablename’ nhé, thực ra thì với câu này không cần dùng ExecuteQuery cũng được, em có thể truy vấn ngay trên table trong DataContext:
var n = (from c in myContext.myTable).Count
Anh Nam ơi cho em hỏi mô hình ứng dụng của LinQ trong 3layer được không anh , em tìm kiếm tài liệu rất lâu rồi nhưng chưa tìm được câu trả lời .Anh cho em vài nhận xét nha ^^! thanks for your help
LINQ to SQL là một phương cách để truy cập dữ liệu, vậy nên nó hoàn toàn dùng được trong mô hình 3 lớp em ạ.
LINQ to SQL thay đổi cách em viết code, chứ không thay đổi kiến trúc phần mềm, vậy nên những kiến thức mà trước đây em có khi viết ứng dụng 3 lớp đều có thể áp dụng lại.
hihihi thanks!!! có thể em đã hiểu ý anh rùi 🙂
Chúc anh sức khỏr nhiều nhiều nha!!!
Nếu có thời gian thì anh viết 1 bài về vấn đề này đi 😀 em nghi nó sẽ rất hấp dẫn 😀
Ờ ờ, rằng hay thì thật là hay, nhưng rảnh một chút đã, he he.
Anh ơi có nick Yahoo không add nick die_for_rock2006 em xin được thỉnh giáo anh chút được không ạ 😀
Trong bài viết tiếp theo, tôi sẽ nói đến một số khái niệm còn lại như: Single Table Inheritance, Deferred/Eager Loading, Optimistic Concurrency, và sử dụng trong hô mình Multi-Tier.
Cái Này A Post Luôn Nhé .Cám ơn Nhiều.
Mấy bài này trong bản gốc là các bài viết độc lập nên anh không dịch em ạ
Ah vậy anh có tài liệu hay và dễ hiểu về:
Single Table Inheritance, Deferred/Eager Loading, Optimistic Concurrency, và sử dụng trong hô mình Multi-Tier.
Chỉ em nhé vì cái Optimistic Concurrency em đọc nhiều tài liệu rùi mà vẫn không hiểu!!!
Ah Cho em hỏi thêm về interface của c# :(cái này cũng khó hiểu weee)
1>nó là cái gì?
2>mục đích nó để làm gi?
3>sử dụng ra sao?
Có 2 kiểu kiểm soát tranh chấp dữ liệu, cách thứ nhất (gọi là pessimistic concurrency) là khi em muốn cập nhật cái gì thì em phải khóa cái đó lại, phòng trường hợp chưa cập nhật xong mà người khác lại sửa, dẫn đến dữ liệu bị sai. Khi nào làm xong thì mở khóa ra, anh khác lúc đó mới sửa được dữ liệu.
Kiểu thứ hai là sau khi đọc dữ liệu xong, nếu em muốn cập nhật thì em phải so sánh dữ liệu lúc cập nhật có còn giống lúc trước em đọc vào hay không, nếu không có nghĩa đã có người khác sửa rồi, và em không được cập nhật nữa. Kiểu này được gọi là Optimistic Concurrency.
Còn cái interface thì khó quá nên em tìm đọc thêm trong các sách dạy C#, Java hay VB.NET, nó vượt quá tầm của blog này rồi :p
Ah cám ơn a,
Trên lí thuyết thì em hiểu nhưng trong thực tế mình phải áp dụng Optimistic Concurrency như thế nào hả a?Nếu a có ví dụ mẫu thì quá tuyệt rùi.
Còn cái interface của c#:vd:IEnumerable,IList,…thì trong sách nói rất chung chung là giống như abstract class,nhưng em chưa thấy được ứng dụng thực tế là để làm gì ?…
Thank for your helping!
Anh oi cho em hoi cach truy van du lieu tu nhieu bang su dung mo hinh 3 lop, chang han du lieu can hien thi trong datagridview la ket hop cua nhieu bang
Em mới Học asp.net Chưa hiểu rỏ nhìu xin mọi người giúp đỡ!!
Code em viết thế này:
public string LayTenDangNhap(string email)
{
var tDN = (from p in data.KhachHangs
where p.Email.ToString() == email
select p).SingleOrDefault();
return tDN.tenkhachHang.ToString();
}
với mong muốn của em là lấy về giá trị “tenkhachHang” nhưng mà khi chạy thì nó báo lỗi:
” An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. “