Cập nhật cơ sở dữ liệu (LINQ to SQL phần 4)


Từ vài tuần trước, tôi đã bắt đầu một loạt bài nói về LINQ to SQL. LINQ to SQL là một O/RM có sẵn trong bản .NET Framework 3.5, và nó cho phép bạn dễ dàng mô hình hóa các CSDL cù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ư để thêm/xóa/sửa dữ liệu.

Dưới đây là 3 bài đầu tiên trong loạt bài này:

Trong bài hôm nay, tôi sẽ nói rõ hơn về cách chúng ta dùng CSDL đã được mô hình hóa trước đây, và dùng nó để cập nhật, chỉnh sửa và xóa dữ liệu. Tôi cũng sẽ cho các bạn thấy các chúng ta có thể thêm các quy tắc (business rule – sau này trở đi tôi sẽ để nguyên từ business rule, vì từ này rõ nghĩa hơn) và tùy biến cách xác thực tính hợp lệ của dữ liệu.

CSDL Northwind được mô hình hóa dùng LINQ to SQL

Trong phần 2 của loạt bài này, tôi đã đi qua các bước để tạo nên mô hình các lớp LINQ to SQL dùng LINQ to SQL designer có trong VS 2008. Dưới đây là sơ đồ lớp đã được tạo cho CSDL mẫu Northwind và cũng sẽ là mô hình được dùng trong bài viết này:

Khi chúng ta định nghĩa mô hình dữ liệu dùng LINQ to SQL designer như trên, chúng ta đã định nghĩa ra 5 lớp mô hình: Product, Category, Customer, Order and OrderDetail. Các thuộc tính của mỗi lớp ánh xạ vào các cột tương ứng trong bảng dữ liệu. Mỗi đối tượng thuộc lớp thực thể sẽ biểu diễn một dòng trong bảng CSDL.

Khi định nghĩa mô hình dữ liệu, LINQ to SQL designer cũng tạo ra một lớp DataContext cung cấp các cách thức để truy vấn và cập nhật lại dữ liệu. Trong mô hình mẫu chúng ta đã định nghĩa ở trên, lớp này được đặt tên là “NorthwindDataContext”. Lớp NorthwindDataContext có các thuộc tính biểu diễn các bảng chúng ta đã định nghĩa trong CSDL (Products, Categories, Customers, Orders, OrderDetails).

Như chúng ta đã xem trong phần 3, chúng ta cũng dễ dàng dùng các biểu thức LINQ để truy vấn và lấy dữ liệu từ CSDL bằng cách dùng lớp NorthwindDataContext. LINQ to SQL sau đó sẽ tự động diễn dịch các biểu thức đó thành các câu lệnh SQL thích hợp để thực thi.

Ví dụ, chúng ta có thể viết biểu thức LINQ như dưới đây để lấy về một đối tượng Product đơn bằng cách tìm dựa trên tên sản phẩm:

Tôi cũng có thể viết thêm một câu truy vấn LINQ dưới đây để lấy về tất cả các sản phẩm từ CSDL mà hiện tại chưa có đơn đạt hàng, và giá tiền nhiều hơn $100:

Chú ý cách tôi đang dùng “OrderDetails” kết hợp với mỗi sản phẩm như một phần của câu truy vấn để chỉ lấy về các sản phẩm không có đơn đặt hàng.

Change Tracking và DataContext.SubmitChanges()

Khi chúng ta thực hiện các câu truy vấn và lấy về các đối tượng như đối tượng product ở trên, LINQ to SQL sẽ mặc nhiên lưu lại vết của các thao tác thay đổi hay cập nhật mà chúng ta thực hiện trên các đối tượng đó (gọi là change tracking). Chúng ta có thể thực hiện bao nhiêu câu truy vấn và thay đổi mà chúng ta muốn bằng cách dùng LINQ to SQL DataContext, và tất cả các thay đổi đó sẽ được lưu vết lại.

Ghi chú: Việc lưu vết LINQ to SQL xảy ra bên phía chương trình gọi, và không liên quan gì đến CSDL. Có nghĩa là bạn không hề dùng tài nguyên trên CSDL, hoặc bạn không cần cài đặt thêm hay thay đổi bất kỳ thứ gì trên CSDL để cho phép làm điều này.

Sau khi đã cập nhật các đối tượng chúng ta lấy từ LINQ to SQL, chúng ta có thể gọi phương thức “SubmitChanges()” trên lớp DataContext để cập nhật lại các  thay đổi lên CSDL. Việc gọi phương thức này sẽ làm cho LINQ to SQL để tính toán động và thực thi các câu lệnh SQL phù hợp để cập nhật CSDL.

Lấy ví dụ, bạn có thể viết câu lệnh dưới đây để cập nhật lại giá tiền và số lượng đơn vị còn lại của sản phẩm “Chai”:

Khi tôi gọi northwind.SubmitChanges() như ở trên, LINQ to SQL sẽ xây dựng và thực thi một câu lệnh SQL “UPDATE” mà nó sẽ cập nhật lại hai thuộc tính của sản phẩm mà chúng ta đã sửa lại như ở trên.

Tôi có thể viết đoạn lệnh dưới đây để duyệt qua danh sách các sản phẩm ít phổ biến và giá cao, sau đó đặt lại thuộc tính “ReorderLevel” = 0:

Khi tôi gọi northwind.SubmitChanges() như trên, LINQ to SQL sẽ tính toán và thực thi một tập thích hợp các phát biểu UPDATE để cập nhật các sản phẩm có thuộc tính ReorderLevel đã bị thay đổi.

Hãy nhớ là nếu giá trị của các thuộc tính của đối tượng Product không bị thay đổi bởi câu lệnh trên, có nghĩa là bản thân đối tượng không bị thay đổi, thì LINQ to SQL cũng sẽ không thực thi bất kỳ câu lệnh UPDATE nào trên đối tượng đó. Ví dụ, nếu đơn giá của đối tượng “Chai” đã là 2 và số san phẩm còn lại là 4, thì việc gọi SubmitChanges() sẽ chẳng làm thực thi bất kỳ câu SQL nào. Cũng vây, chỉ các sản phẩm trong ví dụ thứ hai có ReorderLevel không bằng 0 mới được cập nhật khi gọi SubmitChanges().

Các ví dụ Insert và Delete

Ngoài việc cập nhật các dòng đã có trong CSDL, LINQ to SQL còn cho phép bạn thêm và xóa dữ liệu. Bạn có thể làm được điều này bằng việc thêm/bớt các đối tượng dữ liệu từ các tập hợp bảng trong lớp DataContext, và sau đó gọi SubmitChanges(). LINQ to SQL sẽ lưu vết lại các thao tác này, và tự động thực thi câu lệnh SQL INSERT hay DELETE phù hợp khi phương thức SubmitChanges() được gọi.

Thêm một sản phẩm

Bạn có thể thêm một sản phẩm mới vào CSDL bằng việc tạo ra một đối tượng thuộc lớp “Product”, gán các giá trị thuộc tính, và sau đó thêm nó vào tập hợp “Products” của DataContext:

Khi gọi “SubmitChanges” như trên, một dòng mới sẽ được thêm vào bảng Product.

Xóa các sản phẩm

Cũng như tôi đã nói về việc thêm một sản phẩm mới bằng cách đổi tượng Product vào tập hợp Products của DataContext, tôi cũng có thể làm một cách ngược lại khi muốn xóa một sản phẩm từ CSDL bằng cách xóa nó khỏi tập hợp này:

(RemoveAll đã được thay đổi bằng DeleteOnSubmit trong phiên bản hiện tại)

Chú ý cách tôi lấy một tập hợp các sản phẩm không còn được sản xuất và cũng không có đơn đặt hàng nào bằng cách dùng một câu truy vấn LINQ, rồi sau đó truyền nó cho phương thức RemoveAll của tập hợp Products trong DataContext. Khi gọi SubmitChanges(), tất cả các sản phẩm đó sẽ bị xóa khỏi CSDL.

Cập nhật thông qua các quan hệ

Điều làm cho các trình ORM như LINQ to SQL cực kỳ mềm dẻ là nó cho phép chúng ta dễ dàng mô hình hóa mối quan hệ giữa các bảng trong mô hình dữ liệu. Ví dụ, tôi có thể mô hình hóa mỗi Product trong một Category, mỗi Order để chứa các OrderDetails cho từng mục, kết hợp các OrderDetail với một Product, và làm cho mỗi Customer kết hợp với một tập các Order. Tôi đã biểu diễn cách xây dựng và mô hình hóa các mối quan hệ trong phần 2 của loạt bài này.

LINQ to SQL cho phép tôi tận dụng được ưu điểm của các mối quan hệ trong việc truy vấn và cập nhật dữ liệu. Ví dụ, tôi có thể viết đoạn lệnh dưới đây để tạo một Product mới và kết hợp nó với một category “Beverages” trong CSDL như dưới đây:

(Add đã được thay đổi bằng InsertOnSubmit trong phiên bản hiện tại)

Hãy chú ý cách tôi thêm một đối tượng Product vào tập hợp Products của một Category. Nó sẽ chỉ ra rằng có một mối quan hệ giữa hai đối tượng, và làm cho LINQ to SQL tự động duy trì mối quan hệ foreign-key/primary key giữa cả hai khi tôi gọi SubmitChanges.

Một ví dụ khác cho thấy LINQ to SQL có thể giúp quản lý quan hệ giữa các bảng như thế nào và giúp cho việc lập trình sáng sủa hơn, hãy xem một ví dụ dưới đây khi tôi tạo một Order mới cho một khách hàng đã có. Sau khi đặt giá trị cho ngày chuyển hàng và chi phí cho việc đặt hàng, tôi sẽ tạo tiếp 2 mục chi tiết trong đơn đặt hàng để chỉ đến các sản phẩm mà khách hàng đang muốn mua. Sau đó, tôi sẽ kết hợp đơn đặt hàng với khách hàng, và cập nhật các thay đổi vào CSDL.

(Add đã được thay đổi bằng InsertOnSubmit trong phiên bản hiện tại)

Như bạn thấy, mô hình lập trình trên cho phép thực hiện tất cả các công việc này một cách cực kỳ sáng sủa theo phong cách hướng đối tượng.

Transactions

Một transaction (giao dịch) là một dịch vụ được cung cấp bởi một CSDL (hoặc một trình quản lý tài nguyên khác) để đảm bảo rằng một tập các thao tác độc lập sẽ được thực thi như một đơn vị duy nhất – có nghĩa là hoặc tất cả cùng thành công, hoặc cùng thất bại. Và trong trường hợp thất bại, tất cả các thao tác đã là làm sẽ bị hoàn tác trước khi bất kỳ thao tác nào khác được cho phép thực hiện.

Khi gọi SubmitChanges() trên lớp DataContext, các lệnh cập nhật sẽ luôn được thực thi trong cùng một transaction. Có nghĩa là CSDL của bạn sẽ không bao giờ ở trong một trạng thái không toàn vẹn nếu bạn thực thi nhiều câu lệnh – hoặc tất cả các thao tác bạn làm sẽ được lưu lại, hoặc không có bất kỳ thay đổi nào.

Nếu không có một transaction đang diễn ra, DataContext của LINQ to SQL sẽ tự động bắt đầu một transaction để bảo vệ các thao tác cập nhật khi gọi SubmitChanges(). Thêm vào đó, LINQ to SQL còn cho phép bạn tự định nghĩa và dùng đối tượng TransactionScope của riêng bạn. Điều này làm cho việc tích hợp các lệnh LINQ to SQL vào các đoạn mã truy cập dữ liệu đã có dễ dàng hơn. Nó cũng có nghĩa là bạn có thể đưa cả các tài nguyên không phải của CSDL vào trong cùng transaction. Ví dụ: bạn có thể gửi đi một thông điệp MSMQ, cập nhật hệ thống file (sử dụng khả năng hỗ trợ transaction cho hệ thống file),… và nhóm tất cả các thao tác đó vào trong cùng một transaction mà bạn dùng để cập nhật CSDL dùng LINQ to SQL.

Kiểm tra dữ liệu và Business Logic

Một trong những điều quan trọng mà các nhà phát triển cần nghĩ đến khi làm việc với dữ liệu là làm sao để kết hợp được các phép xác thực dữ liệu và các quy tắc chương trình (business logic). LINQ to SQL cũng hỗ trợ nhiều cách để các nhà phát triển có thể dễ dàng tích hợp chúng vào với các mô hình dữ liệu của họ.

LINQ to SQL cho phép bạn thêm khả năng xác thực dữ liệu mà không phụ thuộc vào cách bạn tạo ra mô hình dữ liệu cũng như nguồn dữ liệu. Điều này cho phép bạn có thể lặp lại các phép kiểm tra ở nhiều chỗ khác nhau, và làm cho mã lệnh sáng sủa và dễ bảo trì hơn rất nhiều.

Hỗ trợ kiểm tra các giá trị thuộc tính dựa trên schema của CSDL

Khi định nghĩa các lớp mô hình dữ liệu dùng LINQ to SQL designer trong VS 2008, chúng sẽ mặc nhiên được gán các quy tắc xác thực dựa trên cấu trúc định nghĩa trong CSDL.

Kiểu dữ liệu của thuộc tính trong các lớp mô hình dữ liệu sẽ khớp với các kiểu dữ liệu tương ứng trong CSDL. Điều này có nghĩa là bạn sẽ gặp lỗi biên dịch nếu cố gắng gán một giá trị kiểu boolean và cho một thuộc tính decimal, hoặc nếu thử ép kiểu dữ liệu một cách không hợp lệ.

Nếu một cột trong CSDL được đánh dấu cho phép mang giá trị NULL, khi đó thuộc tính tương ứng trong mô hình dữ liệu được tạo bởi LINQ to SQL designer cũng cho phép NULL. Các cột không cho phép NULL sẽ tự động đưa ra các exception nếu bạn cố gắng lưu một đối tượng có thuộc tính đó mang giá trị NULL. LINQ to SQL sẽ đảm bảo các cột định danh/duy nhất không bị trùng lắp trong CSDL.

Bạn có thể dùng LINQ to SQL designer để ghi đè lên các quy tắc xác thực dựa trên schema nếu muốn, nhưng các quy tắc này sẽ được tạo ra tự động và bạn không cần làm bất kỳ điều gì để cho phép chúng. LINQ to SQL cũng tự động xử lý các chuỗi escape, do vậy bạn không cần lo lắng về lỗi SQL injection.

Hỗ trợ tùy biến việc kiểm tra giá trị các thuộc tính

Việc kiểm tra dữ liệu dựa trên cấu trúc định nghĩa trong CSDL rất hữu ích, nhưng chỉ được coi như ở mức cơ bản, trong thực tế có thể bạn sẽ gặp phải những yêu cầu kiểm tra phức tạp hơn nhiều.

Hãy xem một ví dụ trong CSDL Northwind, khi tôi định nghĩa thuộc tính Phone thuộc lớp Customer có kiểu dữ liệu là nvarchar. Các nhà phát triển dùng LINQ to SQL có thể viết code giống như dưới đây để cập nhật nó với một số phone hợp lệ:

Vấn đề là đoạn code trên được coi là hợp lệ đứng từ góc độ kiểu dữ liệu SQL, vì chuỗi trên vẫn là một chuỗi nvarchar mặc dù có thể nó không phải là một số phone hợp lệ:

Để tránh việc thêm các số phone kiểu như trên vào CSDL, chúng ta có thể thêm một quy tắc kiểm tra tính hợp lệ vào lớp Customer. Thêm một quy tắc để kiểm tra thực sự đơn giản. Tất cả những gì chúng ta cần làm là thêm một partial class vào và định nghĩa phương thức như dưới đây:

Đoạn code trên tận dụng ưu điểm của 2 đặc tính trong LINQ to SQL:

1) Tất cả các lớp được tạo ra đều là partial – có nghĩa là nhà phát triển có thể dễ dàng thêm vào các phương thức, thuộc tính và thậm chí cả các sự kiện (và đặt chúng trong một file riêng biệt). Điều này làm cho việc thêm các quy tắc xác thực và các hàm phụ trợ vào mô hình dữ liệu và lớp DataContext rất dễ dàng. Bạn không cần cấu hình hay viết thêm các code nào khác để làm được điều này.

2) LINQ to SQL đã tạo sẵn một loạt các điểm mở rộng trong mô hình dữ liệu và lớp DataContext mà bạn có thể dùng để thêm vào các phép kiểm tra dữ liệu trước và sau khi thực hiện các công việc. Nhiều trong số đó ứng dụng một đặc tính ngôn ngữ mới được gọi là “partial method” có trong VB và C# có trong VS 2008 beta 2. Wes Dyer trong nhóm C# có một bài nói về cách các partial method làm việc tại đây.

Trong ví dụ về việc kiểm tra tính hợp lệ dữ liệu ở trên, tôi dùng phương thức OnPhoneChanging, đây là một phương thức sẽ được thực thi bất kỳ lúc nào người dùng gán lại giá trị cho thuộc tính Phone trên một đối tượng Customer. Tôi có thể dùng phương thức này để xác thực giá trị đầu vào theo bất kỳ cách gì tôi muốn (trong ví dụ này, tôi dùng một biểu thức chính quy). Nếu giá trị đã hợp lệ, tôi chỉ đơn giản return và không làm gì cả, khi đó LINQ to SQL sẽ cho là các giá trị này là giá trị hợp lệ, ngược lại tôi có thể phát ra một Exception bên trong phương thức kiểm tra, và phép gán khi đó sẽ không được thực hiện.

Hỗ trợ tùy biến việc kiểm tra tính hợp lệ của thực thể

Việc kiểm tra trên từng thuộc tính như trong các ví dụ trên rất hữu dụng khi bạn muốn kiểm tra giá trị của từn thuộc tính riêng lẻ. Nhưng đôi khi, bạn sẽ cần phải kiểm tra dựa trên nhiều giá trị của các thuộc tính khác nhau.

Hãy xem ví dụ sau, tôi sẽ đặt giá trị cho 2 thuộc tính “OrderDate” và “RequiredDate”:

(Add đã được thay đổi bằng InsertOnSubmit trong phiên bản hiện tại)

Đoạn lệnh trên là hợp lệ nếu chỉ đơn thuần xét từ góc độ ngôn ngữ – nhưng sẽ là không có ý nghĩa khi bạn lại muốn đặt ngày khách hàng yêu cầu trước ngày đặt hàng.

Tin vui là từ bản LINQ to SQL beta 2, chúng ta có thể thêm vào các quy tắc kiểm tra cho từng thực thể để tránh các lỗi kiểu như trên bằng cách thêm một lớp partial cho lớp “Order” và hiện thực hóa hàm OnValidate(), hàm này sẽ được gọi trước khi dữ liệu được đưa vào CSDL. Bên trong phương thức này, chúng ta có thể truy cập và kiểm tra tất cả các thuộc tính của lớp trong mô hình dữ liệu.

Bên trong phương thức này, bạn có thể kiểm tra giá trị bất kỳ thuộc tính nào, và thậm chí có thể truy cập (chỉ đọc) vào các đối tượng liên quan, và có thể phát ra một exception nếu có tồn tại các giá trị không hợp lệ. Bất kỳ một exception nào được phát ra từ phương thức OnValidate() sẽ làm cho việc cập nhật bị hủy bỏ, và hủy bỏ các thay đổi trong transaction.

Tùy biến các phương thức kiểm tra việc thêm/xóa/sửa dữ liệu

Có nhiều lúc bạn muốn thêm các phép kiểm tra khi thêm/xóa/sửa dữ liệu. LINQ to SQL Beta2 cho phép làm điều này bằng cách cho phép bạn thêm vào một lớp partial để mở rộng lớp DataContext và sau đó hiện thực hóa các phương thức để tùy biến các thao tác thêm/xóa/sửa cho các thực thể. Các thức này sẽ được thực thi tự động khi bạn gọi SubmitChanges() trên lớp DataContext.

Bạn có thể thêm các phép kiểm tra thích hợp vào bên trong các phương thức đó – và nếu dữ liệu hợp lệ, LINQ to SQL sẽ tiếp tục lưu lại các thay đổi vào CSDL (bằng cách gọi phương thức “ExecuteDynamicXYZ” của DataContext).

Một trong những điều thú vị là các phương thức phù hợp sẽ được gọi tự động, không phụ thuộc vào ngữ cảnh mà đối tượng được tạo/xóa/sửa. Hãy xem ví dụ sau, ở đây tôi muốn tạo một Order mới và kết hợp nó với một Customer đã có:

(Add đã được thay đổi bằng InsertOnSubmit trong phiên bản hiện tại)

Khi tôi gọi northwind.SubmitChanges() ở trên, LINQ to SQL sẽ xác định là nó cần lưu lại một đối tượng Order, và phương thức InsertOrder sẽ tự động được gọi.

Nâng cao: Xem danh sách thay đổi cho Transaction

Đôi khi bạn muốn thêm các quy tắc kiểm tra mà không thể chỉ dựa trên từng thao tác thêm/xóa/sửa riêng lẻ, thay vào đó bạn phải có thể duyệt qua toàn bộ các thao tác đã thực hiện trong transaction.

Bắt đầu từ bản Beta2 của .NET 3.5, LINQ to SQL cho phép bạn truy cập vào danh sách này bằng cách gọi phương thức DataContext.GetChangeList(). Nó sẽ trả về một đối tượng ChangeList chứa các tập hợp cho các thao tác thêm/xóa/sửa đã được thực hiện.

Một cách tiếp cận là bạn có thể tạo một lớp thừa kế từ lớp DataContext và override phương thức SubmitChanges(). Khi đó bạn có thể lấy ChangeList() cho thao tác cập nhật và thực hiện các phép kiểm tra cần thiết trước khi thực thi:

Xử lý các thay đổi đồng thời với Optimistic Concurrency:

Một trong những vấn đề mà các nhà phát triển phải nghĩ đến trong môi trường đa người dùng là làm thế nào có thể xử lý các thao tác cập nhật trên các cùng một tập dữ liệu. Ví dụ, cho là có hai người dùng đang cùng lấy về một đối tượng product bên trong một ứng dụng, và một người đặt lại giá trị cho ReorderLevel là 0, trong khi người kia đặt lại là 1. Nếu cả hai người dùng đều lưu lại các thay đổi đó vào CSDL, nhà phát triển cần cân nhắc việc xử lý tranh chấp dữ liệu.

Một cách tiếp cận đơn giản là “let the last writer win” (người cuối cùng là người chiến thắng) – có nghĩa là những thay đổi bởi người đầu tiên sẽ bị thay đổi mà không biết. Và điều này thường được coi là một trải nghiệm kém cỏi (và không đúng) – có nghĩa người dùng sẽ cảm thấy khó sử dụng.

Một cách tiếp cận khác mà LINQ to SQL hỗ trợ là dùng mô hình optimistic concurrency – khi đó LINQ to SQL sẽ tự động xác định xem giá trị gốc trong CSDL đã bị thay đổi bở người dùng khác hay chưa. LINQ to SQL sau đó sẽ cung cấp một danh sách các giá trị bị xung đột để người phát triển có thể chọn giải pháp xử lý hoặc có thể yêu cầu người dùng chọn một thao tác nào họ muốn.

Tôi sẽ nói về cách dùng optimistic concurrency với LINQ to SQL trong các bài viết khác.

Dùng SPROCs hoặc tùy biến logic các câu SQL:

Một trong những câu hỏi mà các nhà phát triển (và đặc biệt là các DBA – các nhà quản trị CSDL), những người đã từng viết các thủ tục (SPROC) với các câu SQL tùy biến thường hỏi khi nhìn thấy LINQ to SQL lần đầu tiên là: “làm sao tôi có thể kiểm soát hoàn toàn các câu lệnh SQL được thực thi bên dưới ?”

Một tin tốt là LINQ to SQL có một mô hình cực kỳ mềm dẻo, nó cho phép các nhà phát triển có thể thay thế các câu lệnh củaLINQ to SQL bằng các thủ tục insert, update, delete mà họ tự định nghĩa.

Điều thực sự thú vị là bạn có thể bắt đầu bằng cách định nghĩa mô hình dữ liệu của riêng bạn và để LINQ to SQL tự thực hiện các thao tác thêm/sửa/xóa. Rồi sau đó bạn có thể tùy biến lại mô hình dữ liệu để thực hiện các thao tác cập nhật với các thủ tục hoặc các câu SQL của bạn mà không phải thay đổi bất kỳ đoạn lệnh nào dùng mô hình dữ liệu đó, và cũng chẳng phải thay đổi bất kỳ quy tắc kiểm tra đã tạo trước đó. Điều này cung cấp khả năng tùy biến rất lớn cho bạn khi xây dựng ứng dụng.

Tôi cũng sẽ nói kỹ hơn về cách tùy biến mô hình dữ liệu dùng các thủ tục hay câu lệnh SQL trong một bài viết khác.

Các bạn đang xem bài viết trong loạt bài “LINQ to SQL”, loạt bài này được dịch bởi Đào Hải Nam từ blog ScottGu http://weblogs.asp.net/scottgu/

133 thoughts on “Cập nhật cơ sở dữ liệu (LINQ to SQL phần 4)

  1. Chào Nam,

    Mình là Thảo (Nguyễn Văn Thảo), bạn cùng lớp hồi đại học ở Đà Lạt. Tình cờ mình tìm tài liệu LINQ mà biết được blog của Nam. Dạo này Nam làm gì, ở đâu, vợ con thế nào? Có tài liệu gì về LINQ gửi cho mình với nhé!

    Chúc sức khỏe!!!!

    Nguyễn Văn Thảo
    CHI NHÁNH CTY CP BIA SÀI GÒN – MIỀN TRUNG TẠI PHÚ YÊN.
    Tel: 0905244116
    email: thaonv@sapbeco.com.vn

  2. He he, chào Thảo, Thảo lớp trưởng phải không ?
    Nếu rảnh thì add mình vào Yahoo hoặc Skype nhé, nick là daohainam (cả 2)

  3. Chao anh Hai Nam, em co doc bai viet cua anh va lam theo huong dan, xong khi den phan insert mot doi tuong thi em mac van de nhu sau:
    NorthwindDataContext db = new NorthwindDataContext();
    Product product = new Product();
    product.ProductName = “aaa”;
    db.Products.
    Den luc nay db.products. KO ho tro phuong thuc Add, anh co the giai thich va giup em voi!

    EM cam on anh nhieu!

  4. Các ví dụ này được lấy từ các phiên bản cũ của LINQ to SQL nên có thể không còn đúng trong các phiên bản hiện tại. Trong ví dụ này, em phải thay Add bằng InsertOnSubmit.
    Cảm ơn em đã nhắc, anh sẽ cập nhật lại các ví dụ này cũng như trong các bài tiếp theo.

  5. Em cảm ơn anh nhiều!Bài viết của anh rất hữu ích cho em và các em khác nếu muốn tìm hiểu LINQ!

  6. chưa tìm hiểu gì nhưng xem qua thì linq quả là một ngôn ngữ đáng học, vì nó giúp cho việc lập trình trực quan hơn, câu lệnh nêu rõ mục đích của người viết. Và Linq không chỉ dừng ở việc SQL mà nó là cả nhân NetFramWork, vì vậy mà dữ liệu của Linq không chỉ là các table như SQL mà còn là các item của list, của array và hầu như của mọi thứ có sự tổ chức của IEnumerable. Vậy so sánh linq và F# có gì đó giống giống nhỉ?

  7. anh Nam ơi, cho em hỏi ví dụ như khi ta thực hiện câu truy vấn sau:
    Product myProduct=db.Products.Single(p=>p.ProductName==’chai’);
    Làm sao để mình kiểm tra được có lấy về được product đó không, hay nói cách khác là nếu không tôn tại product có productName là ‘chai’ thì làm sao để kiểm tra.
    Một vấn đề nửa là khi em gọi thực thi một SPROC như sau:

    ALTER PROCEDURE NV_CapNhat
    @tenDangNhap varchar(20) ,
    @diaChi nvarchar (100),
    @dienThoai varchar(11),
    @ketQua char(1) out
    AS
    UPDATE NhanVien
    SET DiaChi=@diaChi,DienThoai=@dienThoai
    WHERE TenDN like @tenDangNhap
    if @@rowcount1 set @ketQua=’f’
    else set @ketQua=’t’

    Trong chương trình em gọi SPROC trên như sau:
    public bool capNhatThongTin(string diaChi, string dienThoai)
    {
    char kiemTra;
    db.NV_CapNhat(m_tenDangNhap, diaChi, dienThoai,ref kiemTra);
    if (kiemTra == ‘t’) return true;
    else return false;
    }

    nhưng khi biên dịch nó lại báo lỗi như sau:
    Error 2 Argument ‘4’: cannot convert from ‘ref char’ to ‘ref char?’
    Em nhờ anh chỉ giúp, cám ơn anh nhiều!

  8. Em là người mới học linq nên cho em hỏi anh Nam vậy khi dùng Linq thì không cần chuỗi kết nối cơ sở dữ liệu nữa hả anh Nam.

  9. @Hien:
    – Hàm Single() sẽ phát ra exception nếu tập kết quả trả về không có đúng 1 kết quả.
    – Em phải sửa “char kiemTra;” thành “char? kiemTra;”, kiểu char là không nullable, cũng như int, bool, long…, có nghĩa là em không thể gán giá trị null cho nó, tuy nhiên trong SQL thì các kiểu dữ liệu đều có thể NULL. do vậy để tương thích, .NET đã đưa ra cú pháp cho phép khai báo các kiểu dữ liệu cho phép mang giá trị null bằng cách thêm dấu ? vào sau.

    @tuabbinhdinh: Vẫn phải có chuỗi kết nối em ạ, nếu em chỉ tạo đối tượng DataContext mà không truyền tham số thì nó sẽ dùng chuỗi kết nối mặc nhiên được tạo khi em design mô hình dữ liệu. Nếu muốn dùng chuỗi kết nối của riêng em thì truyền vào khi tạo đối tượng DataContext.\
    vd:
    NorthwindDataContext context = new NorthwindDataContext(“Server=.;Database=Northwind;Trusted_Connection=True”);

  10. Em bị lỗi này là sao anh Nam

    Error 1 ‘System.Data.Linq.Table’ does not contain a definition for ‘RemoveAll’ and no extension method ‘RemoveAll’ accepting a first argument of type ‘System.Data.Linq.Table’ could be found (are you missing a using directive or an assembly reference?) D:\ProjectC#\LinqToSql\LinqToSql\Form1.cs 44 25 LinqToSql

  11. System.Data.Linq.Table đâu có hàm nào là DeleteAll đâu nhỉ ?

  12. không phải là DeleteAll mà là RemoveAll anh Nam ạ

  13. Không ý em là khi em code nó không ra RemoveAll method và sinh ra lỗi trên

  14. Trong bản em đang dùng hàm RemoveAll đã được đổi thành DeleteAllOnSubmit rồi.

  15. Vay em khong thay ham Add,neu doi thanh ham gi vay anh Nam

  16. Anh Nam ơi, em mới tập làm quen với LinQ, anh có thể giúp em làm một bài mẫu về insert dữ liệu vào database bằng textbox không ạ? Ví dụ thêm 1 sản phẩm.
    Xin cảm ơn…..

  17. Em có thể theo các bước trong loạt bài về LINQ to SQL, trong loạt bài này có đầy đủ các vấn đề em quan tâm.

  18. Cho em hoi trong C# co #region co tac dung gi vay anh Nam?Tai sao phai dung no?

  19. Anh Nam ơi, em có xem lại các bài trước rồi, nhưng em vẫn không hiểu. Ý của em là tạo Proc thêm sản phẩm mới ở đâu trong cửa sổ làm việc, cách gọi Proc đó như thế nào, làm sao để truyền dữ liệu từ các textbox vào database. Nếu viết thường, không phải bằng LinQ thì dễ, nhưng em mới làm quen với LinQ, chưa hiểu các bước thực hiện của nó như thế nào?. Nếu anh có 1 bài mẫu sử dụng Northiwnd, hay một database bất kỳ về phần insert sử dụng LinQ, anh có thể share vào mail, em tham khảo thêm được không ạ.
    Mail:uhcuthe283@yahoo.com
    Nếu bạn nào có làm về phần này, xin giúp dùm tớ nhé…! Thanks

  20. #region được dùng bởi Visual Studio để nhóm các đoạn code lại với nhau, giúp cho việc soạn thảo dễ dàng hơn.

  21. anh Nam ơi, nhờ anh chỉ giúp em cái này với!
    Ví dụ em có một table KhachHang(MaKH, TenKH,DiaChi, DienThoai)
    trong đó MaKH là khoá chính và có kiểu AutoNumber.
    Khi em dung LINQ để insert vào một bản ghi
    KhachHang myKH=new KhachHang()
    myKH.TenKH=tenKH;
    myKH.DiaChi=diaChi;
    myKH.DienThoai=dienThoai;
    db.KhachHangs.InsertOnSubmit(myKH);
    db.SubmitChanges();
    Nếu là trong SQL server ta có thể lấy lại MaKH vừa tạo bằng cách sau câu lệnh insert ta gán: set @maKH=@@identity
    Anh cho em hỏi nếu dùng LinQ thì có cách nào tương tự để lấy về MaKH vừa tạo không ạ?
    Nhờ anh chỉ giúp, cám ơn anh nhiều!

  22. Trong trường hợp này, em hãy mở mô hình dữ liệu ra (file dbml), sau đó click để chọn cột MaKH trong bảng KhachHang, trên cửa sổ Properties đặt lại giá trị thuộc tính Auto Generated Value = true. Khi đó em không cần truyền giá trị cho cột MaKH trước khi Insert. Giá trị tự động sinh ra sẽ được gán trở lại cho đối tượng KhachHang của em:

    KhachHang myKH=new KhachHang()
    myKH.TenKH=tenKH;
    myKH.DiaChi=diaChi;
    myKH.DienThoai=dienThoai;
    db.KhachHangs.InsertOnSubmit(myKH);
    db.SubmitChanges();

    int maKh = myKH.MaKH;

  23. Anh Nam ơi em gặp vấn đề này trong quá trình làm bài tập C#, mong anh chỉ giúp.
    Giả sử em có hai form: frmDangNhap và frmMain
    em muốn khi chương trình chạy thì chạy frmaDangNhap để người dùng đăng nhập. Sau khi người dùng đăng nhập thành công thì cho Hide frmDangNhap đi và Show frmMain lên để làm việc. Khi người dùng click vào nút logout trên frmMain thì đóng frmMain lại và cho show frmDangNhap mà ta đã Hide trước đó lên lại.
    Em đang vương ở chổ không biết làm thế nào để show frmDangNhap đã hide. Nếu có thể mong anh chỉ giúp, cảm ơn Anh!

  24. Cũng đơn giản thôi, mình cũng đã làm cái này rồi, cách thực hiện như sau:
    1. Sau khi User click vào button đăng nhập mà thành công, thì gọi sự kiện tại button đăng nhập:

    private frmMain main = new frmMain();
    private void btnLogin(object sender, EventArgs e)
    {
    if(CheckingLogin())
    {
    // Successful.
    this.Hide();
    main.Show(); // Nhớ là gọi phương thức Show() chứ không phải ShowDialog() vì khi form Main được gọi lại sẽ lỗi.
    }
    }

  25. Sau khi vào formMain, nếu khi nhấn vào nút logout bạn gọi 2 sự kiện:
    1.
    private void btnLogout(object sender, EventArgs e)
    {
    this.close();
    }
    2. Trong phần phương thức tạo lập mặc định, bạn gọi sự kiện:

    public frmMain()
    {
    InitializeComponent();
    this.FormClosed += new FormClosedEventHandler(frmMain_FormClosed);
    }

    private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
    {
    this.Hide();
    frmLogin lg = new frmLogin();
    lg.Show();
    lgFocus();
    }
    Không gọi phương thức ShowDialog() vì nó là phương thức của class DialogResult chứ không phải của Form.
    3. Và để chương trình thực sự được gải phóng trong Process của RAM. Giả sử sau khi logout, user không muốn đăng nhập tiếp mà thoát khỏi chương trình thì phải gọi sự kiện FormClose trong form Login. Trong phương thức tạo lập gọi sự kiện:
    this.FormClosed += new FormClosedEventHandler(frmLogin_FormClosed);

    private void frmLogin_FormClosed(object sender, FormClosedEventArgs e)
    {
    Application.Exit();
    }

  26. Vì lúc đăng nhập thành công ta đã gọi phương thức Hide(). Phương thức Close() form chỉ đóng form hiện tại chứ không thoát hẳn khỏi chương trình khi ta tạo một Instance của một form khác.
    Hoặc để giải phóng tài nguyên (Resources) của chương trình, bạn có thể gọi phương thức this.Dispose() của formLogin để exit.
    Nếu không làm được, bạn có thể add nick của mình: itshare277@ymail.com hoặc e-mail: itshare277@gmail.com.

    ///
    /// Anh Nam ơi, hôm trước em có Add nick anh, nhưng chưa thấy anh Reply lại. Mong anh kiểm tra lại giúp, để có gì em tiện liên lạc. Chắc công việc của anh bận quá nên gặp nhau trên blog rất khó khăn. Cảm ơn anh nhiều. Nick của em (itshare277@ymail.com)
    ///

  27. Cũng khá đơn giản với LINQ. Nếu bạn đã từng thực hiện với mô hình 3 tầng (3 tier) thì sau khi chuyển qua Linq thì nắm rất nhanh cách mà chương trình gọi các phương thức. Để Insert những entities xuống một Record của Database bạn có thể làm như sau:
    Cách 1:
    public static bool InsertProduct(string pname, int pcatid, decimal unitstock)
    {
    bool result = false;
    NorthwindDataContext db = new NorthwindDataContext();
    Product p= new Product()
    p.ProductName = pname;
    p.CategoryID = pcatid;
    p.UnitsInStock = unitstock;
    db.Products.InsertOnSubmit(p);
    db.SubmitChanges();
    int pid = p.ProductID;
    if(i> 0)
    result = true;
    return result;
    }
    }
    Sau đó chỉ cần truyền các tham số trong phương thức trên khi muốn Insert là okie.

    Cách 2:
    Bạn viết một Stored Procedure.
    Sau đó gọi SP và truyền các tham số. Bạn có thể xem lại các bài dịch của anh Nam.

    Nhưng khi dùng LINQ to SQL bạn sẽ không biết cách thực hiện, xây dựng một chương trình có liên quan đến Database, vì bạn chỉ cần truy vấn là xong. Thường thì biểu thức lambda rất dễ hiểu phải không, cũng giống như query trên Database. Cách đó chỉ giúp chúng ta tiết kiệm time, và bạn muốn phát triển thêm kỹ năng lập trình của mình, bạn có thể tham khảo các mô hình lập trình (# không phải phát triển PM) tại trang http://dofactory.com. Cách mình hay dùng là mô hình 3 Tier, Singleton…

  28. Đơn giản với từ khóa #region … #endregion để giúp bạn nhóm các Method, Properties… lại với nhau trong một class cho gọn nếu code của bạn quá dài, chương trình vừa trong sáng, gọn, vd:

    #region Khach Hang

    public static bool ThemKH();
    public static bool XoaKH();
    public static bool SuaKH();

    #endregion

    #region Nhan Vien

    public static bool ThemNV();
    public static bool XoaNV();
    public static bool SuaNV();

    #endregion

  29. À, muốn gọi các SP trong NorthwindDataContext, bên phần Design bạn kéo một SP từ cửa sổ Server Explorer vào phần bên phải của thiết kế. Để gọi được SP đó trong chương trình bạn khởi tạo thể hiện của class: NorthwindDataContext db = new NorthwindDataContext();
    db.StoredProc();
    Cách truyền và duyệt các Param bạn xem lại các bài hướng dẫn của anh Nam.

  30. À mà anh itshare277 ơi, nếu làm như cách của anh thì trong frmDangNhap ta cũng tạo một thể hiện của frmMain và khi đăng nhập thành công thi ta cho show cái thể hiện đó chứ đâu phải show cái form được khởi tạo ban đầu khi ta chạy chương trình, có cách nào để show chính cái form đã được tạo ra khi ta bắt đầu chạy chương trinh không anh?

  31. Đúng rồi, bạn phải tạo thể hiện mới của formMain thì mới Show() được chứ, vì bạn phải đăng nhập thành công thì mới hiện lên form kia mà. Bạn có thể dùng deletegate để điều khiển sự kiện. Ngoài ra bạn có thể dùng biến static để khởi tạo.

    Bên trong frmMain bạn khai báo một biến global:
    public static frmMain main = null;
    và trong phương thức load form Main bạn gán giá trị cho biến này:
    void frmMain(…)
    {
    main = this;
    }

    Bên form đăng nhập bạn gọi sự kiện của button đăng nhập:
    frmMain.main.Show();
    Hi vọng bạn tìm ra nhiều cách để lấy handle của các form. Good luck.

  32. i lộn formDangNhap chứ, làm như cách của anh nghĩa là cái frmDangNhap được tạo ra đầu tiên khi chạy chương trình nó vẫn tồn tại trong bộ nhớ phải không?

  33. Anh Nam ơi, lại làm phiền anh nửa rồi, em gặp phải vấn đề này không biết anh có giúp em được không:
    Em làm một ví dụ đơn giản như sau:

    Trong project Remoting:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace Remoting
    {
    public class GiaoTiep : MarshalByRefObject
    {
    public delegate bool chuyenYeuCau(string str);
    public event chuyenYeuCau ev_YeuCau;
    public GiaoTiep() { }
    public bool yeuCau(string str)
    {
    return ev_YeuCau(str);
    }

    }
    }

    Trong project server:


    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using System.Runtime.Remoting.Services;
    using Remoting;

    namespace Server
    {
    public partial class Form1 : Form
    {
    private Remoting.GiaoTiep m_GiaoTiep_Client_Server = null;
    private ObjRef m_Service = null;
    private TcpChannel m_Channel = null;

    public Form1()
    {
    InitializeComponent();
    }

    private void startListen()
    {
    stopListen();
    try
    {
    m_Channel = new TcpChannel(1500);
    ChannelServices.RegisterChannel(m_Channel);
    m_GiaoTiep_Client_Server = new Remoting.GiaoTiep();
    m_Service = RemotingServices.Marshal(m_GiaoTiep_Client_Server, "GiaoTiep_Client_Server");
    m_GiaoTiep_Client_Server.ev_YeuCau += new GiaoTiep.chuyenYeuCau(xuLyYeuCau);
    }
    catch
    {
    MessageBox.Show(this, "Error starting listening", "Connection Error");
    }
    }

    //---------------------------------------------------------------------------------------------------

    private void stopListen()
    {
    if (m_Service != null)
    RemotingServices.Unmarshal(m_Service);
    if (m_GiaoTiep_Client_Server != null)
    RemotingServices.Disconnect(m_GiaoTiep_Client_Server);
    if (m_Channel != null)
    ChannelServices.UnregisterChannel(m_Channel);
    m_GiaoTiep_Client_Server = null;
    m_Channel = null;
    m_Service = null;
    }

    //---------------------------------------------------------------------------------------------------

    private bool xuLyYeuCau(string str)
    {
    MessageBox.Show(str);
    return true;
    }

    //-------------------------------------------------------------------------------------------------------
    private void Form1_Load(object sender, EventArgs e)
    {
    startListen();
    }
    }
    }

    Trong project Client:


    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Channels;
    using System.Runtime.Remoting.Channels.Tcp;
    using System.Runtime.Remoting.Services;
    using Remoting;

    namespace Client
    {
    public partial class Form1 : Form
    {
    private Remoting.GiaoTiep GiaoTiep_Client_Server = null;
    public Form1()
    {
    InitializeComponent();
    }

    private void btnGuiYeuCau_Click(object sender, EventArgs e)
    {
    string lenh = txtNoiDungYeuCau.Text;
    string URL = "tcp://localhost:1500/GiaoTiep_Client_Server";
    try
    {
    GiaoTiep_Client_Server = (Remoting.GiaoTiep)Activator.GetObject(typeof(Remoting.GiaoTiep), URL);
    bool ketQua = GiaoTiep_Client_Server.yeuCau(lenh);
    if (ketQua)
    {
    MessageBox.Show("Yeu cau thanh cong");
    }
    else
    {
    MessageBox.Show("Yeu cau khong thanh cong");
    }
    }
    catch (Exception ex)
    {
    MessageBox.Show("Loi ket noi: " + ex.Message.ToString());
    }
    }
    }
    }

    Chương trình rất đơn giản, ở form của client có một textbox và một button. Khi chạy chương trình người dùng nhập một nội dung nào đó vào textbox và click button thì ở server sẻ hiển thị messagebox với nội dung vừa nhập ở client.
    Vấn đề em không hiểu ở đây là nếu chạy hai project và test thử thì chạy bình thường, nhưng nếu ta để một thời gian rồi thử lại thì nó báo lỗi kết nối như sau:
    Object ‘/GiaoTiep_Client_Server’ has been disconnected or does not exist at the server
    Không hiểu có phải sau một thời gian nào đó thì server tự hủy kết nối không, Nhờ anh chỉ giúp, nếu có cách khắc phụ nửa thì tốt. Cám ơn anh!

  34. Ừ, đúng rồi, hồi trước mình làm là khi chương trình chạy thì xuất hiện form Login cho người dùng đăng nhập, nếu thành công thì cho vào form Main làm việc, còn nếu sau 3 lần đăng nhập không thành công thì xuất ra câu thông báo sau đó đóng chương trình lại. Có rất nhiều cách để xây dựng một chương trình có form Login, tùy vào yêu cầu thiết kế của phần mềm.

  35. anh Nam ơi! Em gặp phải một vấn đề như thế này không biết anh có giúp được em không:
    Em đang viết một ứng dụng client server, em dùng .Net Remoting để gọi các phương thức từ xa.
    Em dùng LINQ để kết nối cơ sở dữ liệu, khi truyền các đối tượng là các class thông qua các phương thức gọi từ xa thì các class đó phải được khai báo “Serializable” và vì các lớp này có sử dụng lớp DataContext nên buộc lớp DataContext cũng phải khai báo Serializable, và đến lớp DataContext lại được kế thừa từ lớp System.Data.Linq.DataContext cho nên nó báo lỗi:
    “Type ‘System.Data.linq.DataContext’ in Assembly ‘System.Data.linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’ is not marked as serializable.”
    (Thực ra vấn đề này em cũng đang mơ hồ). Nếu được mong anh chỉ giúp em, cám ơn anh nhiều!

  36. @Hien:
    – (remoting) Server sẽ không hủy kết nối em không yêu cầu nó, tuy nhiên trong thực tế anh đã gặp một vài trục trặc trong việc mở cổng/đóng cổng với các hệ thống chạy trên XP/2000. Em nên thử host trên IIS xem sao.
    – Anh không rõ lý do em cần chuyển lớp DataContext để thực thi từ xa, nhưng theo anh đó không phải là ý tưởng hay. Em có thể mô tả rõ hơn về mô hình mà em dùng được không ?
    – (login form) Với các chương trình như vậy, anh thường để form chính là startup form. Khi chạy lên anh sẽ cho hiện form login, nếu login thành công thì anh sẽ thực hiện công việc, ngược lại anh sẽ close form chính và kết thúc chương trình. Trường hợp người dùng muốn login lại thì chỉ đơn giản anh sẽ hiển thị lại form login và thực hiện lại việc đăng nhập là xong.

  37. @itshare277: anh có nhận được tin nhắn của em và đã add em vào YIM friend list.

  38. Dạ, em đang làm một bài tập lớn môn C#, em đang viết chương trình quản lý quán net, nói chung là giống những chương trình quán net hiện này mà các quán internet đang dùng vậy, vấn đề liên quan đến lớp DataContext thì em giải quyết được rồi, nhưng việc kết nối nó tự ngặt sau một vài phút thì em vẫn chưa giải quyết được. như em đã nói bửa trước, khi em chạy chương trình client và server lên rồi test thử kết nối thì OK nhưng nếu để như vậy khoảng 3 đến 4 phút rồi test thử lại thì nó báo là: “Requested Service not found”. Còn việc anh nói “nên thử host trên IIS xem sao” là sao em không biết, anh có thể nói rỏ hơn đượnc không. À còn cái này nửa, khi sử dụng Linq, giả sử ta khai báo một biến db = new DatabaseNameDataContext(), sau đó vì một lý do nào đó mà dữ liệu trong CSDL đã thay đổi thì khi đó nếu ta dùng biến db nói trên để truy vấn dữ liệu thì dữ liệu mà ta thu được là dữ liệu cũ trước đó( chưa được thay đổi) phải không anh? Và nếu như vậy thì làm sao khắc phục nó. Nhờ anh chỉ giúp!!!

  39. – Em kiểm tra xem cái process để host ứng dụng còn chạy không.
    – Dữ liệu chỉ được lấy khi em gọi câu truy vấn.

  40. À ghi chú thêm là dùng .NET remoting để viết Internet Cafe có vẻ như không phải là 1 ý tưởng hay 😀

  41. Dạ, vậy theo anh thì nên dùng cái gì để viết, vì em mới học thôi nên biết cái nào thì dùng cái đó chứ cũng không biết phải lựa chọn cái nào cho phù hợp, tối ưu cả. Nhờ anh chỉ giúp!

  42. Nếu là anh thì anh sẽ làm trực tiếp với socket, sử dụng UDP giúp hiệu năng tốt hơn.

  43. Dạ, mà anh Nam ơi, nếu dùng socket thì mình có truyền các đối tượng từ server tới client và ngược lại được không hay chỉ hổ trợ truyền các chuổi thôi, vì em C# em chỉ tự học thôi nên còn rất nhiều cái không được rõ lắm. Em hỏi nhiều thế Anh không thấy phiền chứ? Chúc anh vui vẽ!

  44. Về cơ bản, nếu dùng socket thì em chỉ có thể truyền các gói dạng mảng byte, việc chuyển đổi kiểu dữ liệu từ mảng byte sang các kiểu khác em sẽ phải tự làm.
    Không có gì phiền cả em ạ, nếu phiền thì anh đã chẳng mở blog, viết bài rồi trả lời làm gì 🙂

  45. Anh cho em hỏi
    Em có 1 bảng account, gồm có username, password và group
    2 khoá chính là username và password
    Em muốn cập nhật thông tin cuả password thôi thì phải làm sao ạ ?
    Đây là code cuả em làm

    QLTTDataContext db = new QLTTDataContext();
    account acc = new account();
    acc = db.accounts.Single(p => p.username == this.tbtendangnhap.Text);
    acc.password = this.tbxacnhan.Text;
    db.SubmitChanges();

    Nhận được báo lỗi như vậy
    Value of member ‘password’ of an object of type ‘account’ changed.
    A member defining the identity of the object cannot be changed.
    Consider adding a new object with new identity and deleting the existing one instead.

    Tuy nhiên, nếu em thay đổi bảng account chỉ còn 1 khoá chính là username thì câu lệnh kia vẫn chạy được
    Vậy em có thể giải quyết vấn đề này ra sao ? (Vẫn giữ nguyên 2 khoá chính cuả bảng đó)
    Nhờ anh giúp đỡ, em xin cảm ơn anh
    Thân !

  46. @Khách:
    – Câu lệnh “account acc = new account();
    acc = db.accounts.Single(p => p.username == this.tbtendangnhap.Text);” hơi thừa, em không cần new account(); mà có thể gán thẳng “account acc = db.accounts.Single(…);”.
    – Theo anh thấy việc em đặt password là khóa chính không hợp lý, chỉ cần username là có thể xác định được các đối tượng riêng biệt rồi, chả lẽ khi cập nhật lại giá trị của mật khẩu, đối tượng đó sẽ trở thành một đối tượng khác hay sao.

    Do vậy, theo anh em nên sửa lại để chỉ mình cột username làm khóa chính thôi.

  47. Anh Nam ơi, giúp em này với.
    Em viết chương trình C# dùng Linq để kết nối với SQL SERVER 2005. Ở máy em thì chạy bình thường.
    Nhưng khi em copy qua chạy ở máy khác thì nó báo lỗi sau:
    An error has occurred while establishing a connection to the server. When connecting to SQL Server 2005, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: Named Pipes Provider, error: 40 – Could not open connection to SQL Server)
    Em đã cấu hình lại chuổi kết nối, và vào SQL Server 2005 Surface Area Configuration để config lại rồi mà sao nó vẫn báo lỗi như vậy.
    Nhờ anh chỉ giúp.
    Em cám ơn anh!

  48. Em kiểm tra lại những thứ sau:
    – Server có đang chạy không
    – Chuỗi kết nối có chỉ đến đúng tên server hay không
    Nếu server và client đang chạy trên 2 máy khác nhau thì kiểm tra tiếp:
    – Server có enable giao thức TCP/IP hay không (hoặc cái khác ngoài Shared Memory cũng được, nhưng cứ coi nhưng mình sẽ dùng TCP/IP) (menu Configuration Tools/SQL Server Configuration Manager)
    – Client có dùng TCP/IP để kết nối hay không.
    – Thử chuyển qua dùng địa chỉ IP thử xem (vì có thể dịch vụ naming resolution bị trục trặc)
    – Kiểm tra filewall trên server có cho phép mở cổng 1433 hay không.


    Cứ thử mấy cái đó đã, nếu vẫn trục trặc thì nghĩ tiếp 😀

  49. Ghi chú thêm: hiện tại máy em đang dùng Named Pipes để kết nối đến server.

  50. Anh Nam cho em hỏi tiếp >”<
    Em có 2 bảng là bomon(bomonID,tenbomon,phongkhoaID) và monhoc(monhocID,tenmonhoc,bomonID,sotiet)
    Quan hệ 1-n giữa 2 bảng, khoá chính lần lượt là bomonID (bảng bomon) và monhocID (bảng monhoc)
    Khoá ngoại là bomonID

    Hiện em muốn update tất cả các giá trị cuả bảng monhoc thỏa điều kiện bomonID = 1 giá trị nào đó (do combobox truyền xuống)
    Update thuộc tính cuả bảng monhoc là bomonID = ""
    Câu lệnh thực hiện như sau :

    var query = from p in db.dm_monhocs
                            where p.bomonID == cbbomonID.SelectedItem.ToString()
                            select p;
                foreach(dm_monhoc dmmh in query)
                {
                    dmmh.bomonID ="";
                    db.SubmitChanges();                
                }
    

    Thì nhận được báo lỗi

    The UPDATE statement conflicted with the FOREIGN KEY constraint "FK_dm_monhoc_dm_bomon". The conflict occurred in database "QLTT", table "dbo.dm_bomon", column 'bomonID'.
    The statement has been terminated.

    Trong bảng monhoc, thuộc tính bomonID đã được allow null. Vậy taị sao em ko thể update thuộc tính đó = "" ?
    Em xin cảm ơn anh nhiều
    Thân

  51. Sao post bài dài ko hiện ra trời >”< Thôi em hỏi ngắn . Em có 2 bảng là bomon(bomonID,tenbomon,phongkhoaID) và monhoc(monhocID,tenmonhoc,bomonID,sotiet)
    Quan hệ 1-n giữa 2 bảng, khoá chính lần lượt là bomonID (bảng bomon) và monhocID (bảng monhoc). Khoá ngoại là bomonID
    Hiện em muốn update tất cả các giá trị cuả bảng monhoc thỏa điều kiện bomonID = 1 giá trị nào đó (do combobox truyền xuống) Update thuộc tính cuả bảng monhoc là bomonID = ""
    Nhưng toàn báo lỗi quan hệ (conflict giữa quan hệ)
    Nếu em delete thông tin cuả bảng môn học có monhoc có bomonID = giá trị do combobox truyền xuống rồi mới delete thông tin trong bảng bomon thì được trong khi update trước delete sau lại báo lôĩ. Vậy làm sao có thể giải quyết trường hợp này ? Em xin cảm ơn

  52. Lỗi quan hệ vì trong bảng bomon của em không có dòng nào có giá trị bomonID bằng giá trị em cập nhật mới.
    Vì monhoc.bomonID tham chiếu sang bomon.bomonID nên nếu cập nhật lại giá trị của bomonID trong bảng monhoc, em phải cập nhật thành một giá trị nào đã tồn tại. Hoặc nếu muốn xóa một dòng trong bomon, em phải đảm bảo không có dòng nào trong monhoc tham chiếu đến nó (có monhoc.bomonID = bomon.bomonID).

  53. Nam cho mình hỏi một câu nhé.
    lúc mình muốn add thêm một product và mình gõ
    orthWindDataContext northwind = new NorthWindDataContext();
    Product pro = new Product();
    pro.ProductName = “Vang da lat”;
    pro.UnitPrice = 999;
    pro.UnitsInStock = 1;
    pro.Category = 1;
    northwind.Products.
    thi không thấy phương thức Add ở trong cái cửa sổ lệnh tự động xổ xuống. Tại sao vậy nhỉ?

  54. Anh Nam ơi, Em đang viết một chương trình bằng C#, em muốn khi khởi động chương trình, form login sẻ chạy và nhốt chuột trong đó luôn, không cho người dùng làm gì khác cả nếu không đăng nhập. Anh có biết cách làm không chi giúp em với. Em cám ơn anh!

  55. Anh Nam cho em hỏi, em có đoạn code như sau
    string truyvan = “select * from order”;
    IEnumerable results = db.ExecuteQuery(@truyvan);
    dgvtim.DataSource = results.ToList(); //dgvtim là datagridview

    Nếu câu truy vấn cuả em lấy dữ liệu từ 2 bảng trở lên thì phải làm sao ạ ?
    Em xin cảm ơn

  56. string truyvan = "select * from nv_hoso";            
    IEnumerable results = db.ExecuteQuery(@truyvan);            
    dgvtim.DataSource = results.ToList(); (dgvtim là datagridview)
    

    Đoạn code ở trên

  57. Hình code đây ạ, em type 1 số kí tự như dấu lớn hơn nó bị mất đi nên đoạn code ko đúng, nhờ anh xem hình dùm em

    Em cảm ơn

  58. Nếu viết theo cách thi hành 1 chuỗi như em thì rất đơn giản, em cứ viết câu lệnh SQL như bình thường, biến results thì em đừng xác định kiểu mà dùng var rồi truyền results thẳng vào DataSource.
    Anh chưa kiểm tra nhưng anh nghĩ là được.

  59. Anh ơi, em có xem trên MSDN thì cái ExecuteQuery cần 2 tham số, anh có thể chỉ cụ thể cho em được ko ạ ?
    Em muốn chạy câu truy vấn mà truy xuất từ 2 bảng trở lên dùng ExecuteQuery
    Em cảm ơn anh

  60. Tham số thứ nhất là câu lệnh SQL, tham số thứ hai là một mảng các giá trị để định dạng dựa trên tham số thứ nhất. Cách dùng y hệt như khi em dùng lệnh String.Format().


  61. Anh chỉ giúp em cách dùng được ko ạ, với câu truy vấn như thế ? Em chậm tiêu quá >”<
    Lại phiền anh nữa
    Em cảm ơn ạ

  62. Anh Nam ơi cho em hỏi cái này với.
    Em sử dụng Linq để kết nối csdl. Nhưng khi làm report(Crypto Report) động, thì Datasource gán vào cho report phải là DataTable, DataSet hoặc DataReader. anh cho em hỏi trong trương hợp này mình phải sử lý thế nào?
    Anh cho em hỏi luôn, giả sư em có một câu truy vấn sử dụng Linq như sau:
    var giaoDich= from gd in db.GiaoDichs
    where gd.NhanVien==”NhanVien”
    select gd;
    Bây giờ có cách nào chuyển dữ liệu lấy về được trong biến giaoDich trên vào trong một DataTable không?

  63. @Nhat: Để chuyển thì chỉ có cách tạo một biến DataTable, add các DataColumn vào, sau đó duyệt qua tập kết quả do LINQ trả về và thêm các DataRow vào DataTable vừa tạo.
    Nhưng nếu là anh thì anh sẽ không dùng LINQ trong trường hợp này. Đừng cứng nhắc quá mà tự làm khó mình 🙂

  64. @Khách: Thông thường em nên hạn chế dùng theo kiểu ExecuteQuery, vì như vậy em không tận dụng được các ưu điểm của cấu trúc ngôn ngữ LINQ. Thường thì chỉ 10% trường hợp là phải dùng kiểu đó.
    Với câu truy vấn của em, nếu anh viết theo kiểu LINQ thì anh sẽ viết như sau:

    var q = from o in db.orders inner join c in db.customers on o.customerId equals c.customerId
    select new { Customer = c, Order = o};

    Tập kết quả q sẽ là một tập hợp các đối tượng có kiểu vô danh (anonymous) với 2 thuộc tính là Customer và Order. Rõ ràng cách viết này sáng sủa hơn cách của em nhiều 🙂

  65. Vậy a Nam cho em hỏi,em đang làm chức năng search của 1 phần mềm nhỏ(search theo vài option, vd là 8),em xử lí theo hướng thay đổi (thêm vào hoặc ko thêm vào) câu truy vấn rồi thực hiện câu truy vấn để ra kết quả tìm kiếm
    Nếu như em thực hiện câu truy vấn như kiểu var biến = from … where … select … thì em ko biết phải thêm vào hay ko thêm vào câu truy vấn đó như thế nào cả (chắc do em chưa biết)
    Vì lí do đó nên em đành chữa cháy = ExecuteQuery để dễ thay đổi câu truy vấn cuả em. Mong được anh chỉ 1 giải pháp cho em. Em cảm ơn anh

  66. He he, có khi là em đang ở trong 10% nho nhỏ kia rồi 😀

  67. ôi anh ơi, thế thì phải làm sao để truy xuất 2 bảng khi dùng cái ExecuteQuery T_T T_T
    Hay anh có giải pháp nào cho em khi search ko ạ ? >”<
    Làm phiền anh quá

  68. Chào anh !
    anh có thể giúp em một chút được không.
    em đang làm 1 Projects sử dụng linq kết nối 3 tầng , kiểu n-layer nếu đưa dữ liệu như trên thi em làm được
    nhưng làm 3 tầng thì em chưa làm được anh có thể chỉ cho em ko có ji nhắn tin cho em nhé
    mong hồi âm của anh!

  69. Em có thể dùng LINQ to SQL data model như lớp Data Access.

  70. Hi a Nam, cho e hỏi phương thức update của e như sau:

    public void Update(TEntity changedEntity, TEntity originalEntity)
    {
    //Init DataContext
    try
    {
    _TContext.GetTable().Attach(changedEntity, originalEntity);
    //submitchanges
    }
    catch (Exception ex)
    {
    throw ex;
    }
    }

    Khi e thực thi thì báo lỗi như thế này:
    “An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported.”

    Còn khi e code như thế này //_TContext.GetTable().Attach(changedEntity,true);

    Thì nó báo lỗi thế này:
    “An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.”

    Em chưa hiểu rõ lắm nó ý nghĩa nó throw lỗi là gì? A có thể support e ko ạ? Tks a nhiều

  71. Bạn cho hỏi tại sao mình không thể can thiệp vào danh sách các thao tác đã thêm vào datacontext
    danh sách các thao tác trả lời là readonly ?

    dim inserted=me.GetChangeSet().Inserts()

    ínserted.clear() ->loi

  72. Các tập dữ liệu trong ChangeSet được dùng bởi LINQ to SQL, em không thể can thiệp trực tiếp, thậm chí việc đọc cũng có một số hạn chế.
    Nếu không muốn commit thì dừng gọi đến SubmitChanges, cũng như phải can thiệp gián tiếp vào các tập ChangeSet này thông qua các phương thức như InsertOnSubmit…

  73. a Nam ơi, em có tý khó khăn muốn hỏi a . E đang viết một chương trình mà sau khi nghiên cứu tất cả các bài của a e thấy mình hok áp dụng được LinQ, a có cách nào hok chỉ e với . Chả là e định viết một ứng dụng nhưng trong CSDL các table liên tục được sinh ra theo các tháng và các năm, mà LinQ bắt buộc phải add các table vào trình thiết kế a có thể giúp em làm thế nào để áp dụng được LinQ trong trường hợp này không ah ?

  74. Vấn đề của em có lẽ không phù hợp để dùng LINQ.
    Anh cũng từng gặp nhiều phần mềm dùng cách như em nói, chủ yếu là các phần mềm kế toán, quản lý…
    Cá nhân anh thì không ủng hộ cách thiết kế như vậy.

  75. Anh Nam cho hỏi, khi mình câu lệnh InsertOnSumit() nó sẽ insert dữ liệu vào, nhưng tại sao khi em tạo tiếp 1 đối tượng khác gọi lại câu lệnh này, thì record cũ trong bảng bị mất update lại cái mới. Em ko muốn bị mất cái dữ liệu cũ khi insert tiếp 1 cái mới vào, thì em là sao vậy anh Nam?
    Cám ơn anh.

  76. Anh Nam ơi cho em hỏi, khi mình câu lệnh InsertOnSumit() nó sẽ insert dữ liệu vào, nhưng tại sao khi em tạo tiếp 1 đối tượng khác gọi lại câu lệnh này, thì record cũ trong bảng bị mất update lại cái mới. Em ko muốn bị mất cái dữ liệu cũ khi insert tiếp 1 cái mới vào, thì em là sao vậy anh Nam?
    Cám ơn anh.

  77. Anh Nam ơi! Single() và SingleOrDefaul ()
    Dùng Single() khi nó trả về duy nhất 1 kết quả có trong CSDL,thường thì sd nó trong hàm sửa(vì hàm sửa ta biết chắc chắn rằng thuộc tính ta muốn sửa nó phải tồn tại trong CSDL)
    vd :
    var cust = linq.Customers.Single(c => c.CustomerID = “ALFKI”)
    Vì CustomerID là khóa chính của bảng Customers lên nó trả về kết quả duy nhất có ID như vậy và ta thực hiện công việc sửa
    Nhưng nếu giờ em thay bằng câu lệnh
    var cust = linq.Customers.Single(c => c.CustomerName = “smilesr”)
    Do CustomerName không phải là khóa chính lên có thể nhiều kết quả được trả ra.như vậy dùng Single là sai
    SingleOrDefaul() thường sử dụng cho hàm tìm kiếm ,vì hàm này trả ra kết quả là có hoặc hok có (,hoặc có thể có nhiêu)

  78. Nếu đã dùng Single thì chắc chắn phải có đúng 1 dòng trả về, nếu không sẽ bị lỗi. Trường hợp dữ liệu có nhiều dòng thì phải lập vòng lặp để cập nhật lại các dòng thỏa mãn.

  79. anh Nam cho em hỏi! sao đoạn code dưới đây của em khi chạỵ thì nó ok hết! mà sao khi em coi trong csdl lại 0 có gì hết vậy. Em mới tập tễnh linq nên thấy cái gì cũng lạ hết. Mong anh giúp dùm!

    Code:
    QLDPDataContext db = new QLDPDataContext();

    private void button1_Click(object sender, EventArgs e)
    {

    THUOC thuoc = new THUOC();
    thuoc.TenThuoc = txtTenThuoc.Text;
    thuoc.DonViTinh = txtDonViTinh.Text;
    thuoc.DonGia = Convert.ToDecimal(txtDonGia.Text.ToString());
    thuoc.Chucnang = txtChucNang.Text;
    thuoc.HanSuDung = txtHSD.Text;
    thuoc.MaDanhMuc = txtDanhMucThuoc.Text;
    thuoc.MaNhomThuoc = txtNhomThuoc.Text;
    thuoc.NgaySanXuat = txtNgaySX.Text;
    thuoc.MaHangThuoc = txtHangThuoc.Text;
    db.THUOCs.InsertOnSubmit(thuoc);
    db.SubmitChanges();
    MessageBox.Show(“thanh cong”);

    }

  80. Anh cũng không rõ vì nhìn thì thấy vẫn ok, nhưng hình như không thấy có cột nào là PK thì phải???

  81. cột pk nào anh? em 0 hiểu ý anh lắm!

    em cũng làm thử update rồi! Đoạn code như sau:

    private void btnUpdate_Click(object sender, EventArgs e)
    {
    var result = from c in db.THUOCs where c.TenThuoc == txtUpdateTenThuoc.Text select c;
    foreach (var i in result)
    {
    i.DonGia = Convert.ToDecimal(txtUpdateDonGia.Text.ToString());
    }
    db.SubmitChanges();
    MessageBox.Show(“ok”);

    cùng form với câu trên, em có lớp qldpdatacontext như trên lun.

    nó cũng chạy ok, nhưng csdl 0 thay đổi. Em 0 hiểu là có vấn đề gì nữa?

  82. Ý anh là anh không biết kiểu THUOC có cột nào là khóa chính.
    Anh cũng ngạc nhiên là sao không được :(, em có thể gửi cái project đó cho anh không?

  83. à, em hiểu ý anh rồi. cột Khóa chính của THUOC là tên thuốc. em đọc bài của anh rồi nghiệm làm theo, em 0 thấy sai xot nên thấy lạ. 0 bik là sai sao nữa?

    Em đang làm đề án QUẢN LÝ DƯỢC PHẨM dùng linq. Vậy em làm phiền anh vậy.

    http://www.mediafire.com/myfiles.php

    anh lên đây tải nha. em đang từ từ những cái đơn giản để làm 2 chức năng chính là nhập thuốc từ nhà cung cấp và do khách hàng trả.

    Chắc em còn phải hỏi anh nhìu.

    mà Anh có nick yahoo thì cho em xin với. Có gì em tiện hỏi. Em còn gà mờ quá.

    Nick em là: c3c_sumio@yahoo.com

  84. Cho em hỏi có cách nào để đổ dữ liệu từ SQL vào Datatable bằng linQ ko?
    VD: em có 1 bảng hocsinh và em muốn đưa hết dữ liệu này vào Datatable bằng truy vấn của linQ.

  85. Anh Nam có thể hướng dẫ cho em làm cái giỏ hàng và cho phép khách hàng đặt hàng sau đó isert xuống cơ sở dữ liệu bàng linq được không? Em đang làm đồ án môn học và em đang định làm bằng linq mong anh Nam chỉ giáo cho.

  86. Giỏ hàng thì không liên quan đến LINQ hay không em ạ, cứ khi nào người dùng chọn một món hàng thì em thêm nó vào một biến trong session, ví dụ như một Dictionary chẳng hạn, key là ID của món hàng, value là 1 integer lưu số hàng người dùng đã chọn cho sản phẩm tương ứng.
    Khi nào người dùng check out thì lấy cái Dictionary đó, duyệt qua từng thành phần bên trong, tính toán rồi tạo order thôi.
    Cách website TMĐT hiện nay cũng có thể dùng CSDL để lưu giỏ hàng thay cho session, như vậy thì lần sau người dùng có thể thoát ra và lần sau vào mua tiếp, nhược điểm là thêm tải cho CSDL (cũng không đáng kể nếu so với quy mô của toàn hệ thống).
    Em nên vào google để tìm các ứng dụng mẫu về giỏ hàng, có thể tham khảo thêm:
    http://articles.sitepoint.com/article/net-shopping-cart-datatables
    http://www.codeproject.com/KB/user-controls/shopcart.aspx

  87. @cubi: Cái link em đưa là files của riêng em, sao anh thấy được 😦
    Nếu được thì em send qua mail cho anh đi: daohainam@yahoo.com
    Nick Y!: daohainam

  88. hi anh, em muốn hỏi anh một câu được không? hiện tại e đang gặp một vấn đề liên quan đến cập nhật cơ sở dữ liệu. em có 1 bảng A quan hệ với bản B theo kiểu 1-n.
    Khi em Update bảng A thi Linq của e đã detach các quan hệ của nó rồi sau đó cập nhật dữ liệu của A. Tuy nhiên ở bảng B khi đó khóa ngoại lại bị Null? em chưa biết làm thế nào để khắc phục lỗi này. Mong anh chỉ giáo

  89. Anh Nam cho em hỏi, dùng linq để query 1 bảng dữ liệu về:
    var aTable = from p in …
    select p;

    bind vào một datagridview:
    aDatagridview.DataSource = aTable;

    Khi em add/update/delete một row và gọi SubmitChanged.
    Vấn đề là aDatagridview của em nó không tự động refresh. Vậy làm sao để refresh cho aDatagridview mà không cần query và bind lại? và làm sao để select aDatagridview vào đúng row vừa cập nhật (vd: vào dòng mới được add). Cảm ơn anh!

  90. Theo em nói thì anh đoán em dùng datagridview trên winform (vì nếu trên webform thì em phải refress cả trang). Các anh thường dùng chỉ là đặt DataSource của DataGridView = null, sau đó gán lại :), nó có vẻ củ chuối nhưng … chạy :))

  91. Đúng là em dùng trên winform. Vậy còn làm sao em biết được hàng nào là hàng vừa Add? để select vào đúng hàng đó trong aDatagridview sau khi gán lại DataSource.

  92. Anh Nam giúp em một chút với, em muốn gắn nối dữ liệu vào combobox, lấy danh mục các tỉnh thành, nhưng em làm mãi ko ra:
    GridViewComboBoxColumn combott = (GridViewComboBoxColumn)grid_QHTT.Columns[“idtt”];
    KD_DIAOCDataContext db = new KD_DIAOCDataContext();
    var tinhthanh = from tt in db.DM_TINHTHANHs;
    combott.DataSource = tinhthanh;
    combott.ValueMember = tinhthanh.Select(c => c.IDTT)); //kiểu int
    combott.DisplayMember = tinhthanh.Select(c => c.TENTT);//kiểu string

    Cảm ơn anh.

  93. Cái ValueMember và DisplayMember phải là tên của các cột trong table DM_TINHTHANH chứ nhỉ.
    ValueMember là tên cột chứa giá trị, ví dụ “IDTT”
    DisplayMember là tên cột chứa giá trị được hiển thị, ví dụ “TENTT”

  94. Anh Nam sao e làm trong silverlght khi thêm, xóa, sửa nó không cập nhật tức thời mà nó phải reload lại mới thấy kết quả.Có cách nào thấy được kết quả thực thi liền xuống lưới không? mong anh Nam và các bạn có gặp qua giúp đở.

  95. Ở đâu cũng vậy thôi em ạ, nếu Silverlight phải theo dõi database để xem có gì thay đổi không thì chắc chẳng mấy chốc mà website của em … đi bộ đội 😀

  96. anh ơi tại sao em lam báo cáo report lại báo lỗi này hả anh :{“DataSet does not support System.Nullable.”}
    lệnh của em thế này :
    var query = from p in db.view_CongNos
    select p;
    CongNo _congno= new CongNo();
    _congno.setdatasource(query);
    crytal.reportsource=_congno;
    anh giúp em với

  97. Anh nam ơi!
    Hiện tại e có 1 vấn đề về sử dụng LINQ khi mà e dùng dữ liệu của 2 bảng cho vào 1 bảng. Cụ thể là 2 table Bode và Cauhoi, trong bảng Bode chứa MaBoDe, trong Cauhoi chứa MaCauHoi, và em muốn Mabode và Macauhoi sẽ nằm trong Table Chitietbode. Và với yêu cầu là 1 mã bộ đề sẽ có nhiều Mã câu hỏi, e đã dùng mọi cách để làm nhưng mà ko hiểu sao nó chỉ add được 1 dòng thôi. Nếu e cố gắng sư dụng vòng lặp thì nó sẽ báo lỗi là ko thể thêm vào một thưc thể đã tồn tại. Mong anh Chỉ e với.
    Email: nguyenhaidangduy@yahoo.com

  98. Theo thiết kế của em thì hai bảng Bode và Cauhoi có quan hệ nhiều-nhiều, 2 cột có trong bảng ChitietBode sẽ phải tham gia và làm khóa chính.
    Có lẽ em nên kiểm tra lại các cột khóa của các bảng. Nếu chỉ là quan hệ 1-n (1 mã bộ đề sẽ có nhiều Mã câu hỏi) thì thiết kế của em không hợp lý.

  99. Anh Nam cho em hỏi chút. Em có 1 cơ sở dữ liệu về sinh viên, trong chức năng tìm kiếm em có cho chọn là tìm theo mã số sinh viên hay mã lớp. Nếu dùng LINQ để truy vấn thì em làm được nhưng em muốn nó xuất ra một form kết quả mà không biết làm thế nào ? Anh Nam giúp em nhé.

  100. Anh ơi..Cho em hỏi nếu muốn add csdl bằng Procedure thì mình nên làm thế nào

  101. cho em hỏi,dùng LINQ sao không lưu được vào database ? khi em submitchanges() thì nó chỉ lưu 1 file tạm nào đó,rồi 1 hồi lại mất,trong solution explore cái folder file data.mdf bị close là em bị mất dữ liệu vừa nhập,nói chung là em k lưu được dữ liệu vào data
    trả lời em sớm maimai_1_bonghinh7@yahoo.com
    thanks

  102. Chào anh Nam, em đang gặp 1 rắc rối không biết cách giải quyết.
    Tất cả các thao tác Insert, Update và Delete sử dụng LINQ to SQL trong Project C# của em đều chay tốt khi run chương trình. Tuy nhiên, dữ liệu dường như chỉ thay đổi trong context thì phải. Khi Update dữ liệu thì trong chương trình có sự thay đổi rõ ràng tuy nhiên trong CSDL lại không có sự thay đổi mặc dù em đã có dòng lệnh SubmitChange(). Và Insert, delete cũng như vậy.
    Anh có thể giúp em fix lỗi này được không ạ!!!
    Cảm ơn anh

  103. Thưa anh!
    Tại sao khi gọi SubmitChanges(), Cở sở dữ liệu lại được thêm vào một bản ghi trong khi dữ liệu nhập vào không hợp lệ? Cụ thể ở đây là một trường em đặt là “Not Null” nhưng khi giá trị của trường đó nhập là Null mà vẫn có thể thêm vào được bản ghi mới.

  104. Thưa anh!
    Tại sao khi gọi SubmitChanges(), Cở sở dữ liệu lại được thêm vào một bản ghi trong khi dữ liệu nhập vào không hợp lệ? Cụ thể ở đây là một trường em đặt là “Not Null” nhưng khi giá trị của trường đó nhập là Null mà vẫn có thể thêm vào được bản ghi mới.

  105. Chắc có gì nhầm lẫn ở đây chứ nếu đã là NOT NULL thì làm sao em đưa NULL vào được. Việc kiểm soát này được thực hiện bởi CSDL, không phải do LINQ to SQL.

  106. Anh ơi cho em hỏi hiện tại em đang xây dựng quản lý siêu thị g7,đến màn hình phiếu bán hàng thì gặp một số rắc rối.Cụ thể là khi mà em muốn chọn cbTenSanPham xong thì em chuyển sang tbSoLuong nhấn số lượng cần mua vào sau khi chọn 2 cái đó em nhấn btThem thì lập tức Thông tin sản phẩm và số lượng sẽ nhập datagridview nhưng em chưa làm được chổ đó mong anh giúp đỡ.Chân thành cảm ơn anh

  107. Cảm ơn anh ,anh có thể cho em biết khi nào dùng InsertOnSubmit vàSingleOrDefault ,where.tolist().
    Cảm ơn anh nhiều

  108. (trong C#)
    A cho mình hỏi, nếu đối với sql thì khi lấy giá trị lớn nhất, nhỏ nhất, giá trị trung bình,..thì mình có thể dùng các hàm tương ứng trong sql như là: Max,Min,Avg,…Trong khi nó ngôn ngữ linq là ngôn ngữ mô hình hóa CSDL, có thể truy vấn tương tự như CSDL (khi biên dich). Vậy trong linq thể sử dụng hàm giống như SQL để lấy giá trị lớn nhất hay nhỏ nhất,..Nếu được thì mình làm ntn? Nếu ko thì dựa vào linq làm sao lấy giá trị lớn nhất từ sql? (Không dùng PROC).
    Ví dụ: Mình có một bảng CSDL (MaNV(int),TenNV), vậy làm sao chọn MaNV lớn nhất!
    Rât mong nhận được sự giúp đỡ của a!

  109. a Nam cho em hỏi cái này tí! Khi em kết nối với CSDL (dùng sql server) thì mỗi khi truy vấn bằng linq như đỗ kết quả vào “datasource” của lưới hoặc dùng foreach để duyệt kết quả trả về thì nó cứ báo lỗi:
    ‘Cannot open database “db_dec” requested by the login. The login failed.
    Login failed for user ‘Computer-PC/Computer’. Trong khi đó cũng với csdl đó cũng là vs 2010 nhưng khi copy qua máy khác thì nó lại chạy bình thường.
    Em đã test port và sửa các lỗi thường gặp trong sql rồi nhưng không có kết quả gì?Mong a và mọi người giúp đỡ!

  110. Em kiểm tra thông tin đăng nhập, lỗi này nghĩa là em vẫn kết nối được đến máy chủ CSDL nhưng việc đăng nhập thất bại.

  111. Chào anh Nam…

    Rất cảm ơn anh vì đã có những chia sẻ rất bổ ích…

    Nhờ anh giải thích giúp đoạn code này bị lỗi như thế nào và cách khắc phục ra sao:

    public partial class User
    {
    public static List GetAll()
    {
    DataClassesDataContext db = new DataClassesDataContext();

    return db.tblUsers.ToList();
    }

    public static User GetUser(int id)
    {
    DataClassesDataContext db = new DataClassesDataContext();
    return GetUser(id, db);
    }

    public static User GetUser(int id, DataClassesDataContext db)
    {
    return db.tblUsers.Single(e => e.UserID == id); ( Báo lỗi ở dòng code này, chi tiết lỗi ở bên dưới)
    }
    }

    Chi tiết lỗi: Error 14 Cannot implicitly convert type ‘System.Linq.IQueryable’ to ‘System.Linq.IQueryable’. An explicit conversion exists (are you missing a cast?)

    Em không hiểu tại sao ở trên em dùng ToList() thì không vấn đề gì, bên dưới dùng Single() thì lại báo lỗi. Mong anh giải đáp giúp…

    Cám ơn anh rất nhiều….

  112. anh Nam ơi em có phần này mún hỏi:
    em có bảng công việc gồm có 2 cột mã công việc và tên công việc
    em mún thêm vào công việc mới
    public void ThemCongViec(string MaCongViec, string NoiDung)
    {
    CongViec cviec = new CongViec();
    cviec.MaCongViec = int.Parse(MaCongViec);
    cviec.NoiDung = NoiDung;

    dbs.CongViecs.InsertOnSubmit(cviec);

    dbs.SubmitChanges();

    }
    nhưng sao khi chạy nó toàn lấy mã cv tăng 1 so với trước chứ không lấy mã công việc em nhập đô?
    anh giải thích giùm em??

  113. Chào anh Nam,
    anh cho em hỏi neeuss trong bảng mình cập nhật có 1 khóa thì mình dùng Single nhưng nếu bảng dữ liệu của em có khóa ngoại thì khi sửa em dùng câu lệnh gì?Mong anh chỉ giúp em chỉ vừa học Linq trong kì này nên còn tệ lắm.

  114. Chào Anh Nam.
    Xin cho hỏi về việc kết nối vào SQL. Trong kết nối vào SQL bằng ADO khi lấy dữ liệu xuống Dataset xong thì ta ngắt kết nối, xử lý gì đó xong thì kết nối lại để update phần thay đổi vào SQL còn LinQ thì sao Anh, khi nhiều máy kết nối một lượt thì có quá tải không Anh? Rất mong thông tin từ Anh!

  115. Chào Anh Nam.
    Xin cho mình hỏi muốn thống kê theo Ngày, tháng, Năm mình phải làm sao? mong anh giúp đỡ

Leave a comment