[ADO.NET Tutorial] Lesson 05: Làm việc với Disconnected Data – DataSet và SqlDataAdapter

(The C# Station ADO.NET Tutorial)

Lesson này hướng dẫn cách làm việc với dữ liệu ngắt kết nối (disconnected data) với DataSet và SqlDataAdapter. Qua đó, bạn sẽ biết cách sử dụng DataSet, SqlDataAdapter  và hiểu tại sao cần phải làm việc với disconnected data.

Giới thiệu

Một DataSet là một đối tượng chứa dữ liệu trong bộ nhớ và có thể gồm nhiều bảng. DataSet chỉ chứa dữ liệu chứ không tương tác với nguồn dữ liệu. Thay vào đó, SqlDataAdapter sẽ được dùng để quản lý các kết nối với nguồn dữ liệu và cho chúng ta chế độ làm việc disconnected. SqlDataAdapter mở  một kết nối chỉ khi cần thiết và đóng nó ngay sau khi tác vụ được hoàn thành. Ví dụ, SqlDataAdapter thực hiện các tác vụ sau, khi đổ dữ liệu vào DataSet:

  1. Mở kết nối
  2. Đổ dữ liệu vào DataSet (Fill)
  3. Đóng kết nối

Và thực hiện các công việc sau, khi cập nhật dữ liệu nguồn với thay đổi của DataSet:

  1. Mở kết nối
  2. Ghi thay đổi từ DataSet vào dữ liệu nguồn (Update)
  3. Đóng kết nối

Giữa hai thao tác Fill và Update, các kết nối với nguồn dữ liệu được đóng lại và bạn có thể tự do ghi, đọc dữ liệu với DataSet. Đây chính là cơ chế của mô hình làm việc với disconnected data. Bởi vì ứng dụng sẵn sàng kết nối khi cần thiết, ứng dụng trở nên dễ phát triển hơn.

Hai kịch bản minh họa cho lý do tại sao bạn cần làm việc với disconnected data: người dùng làm việc không cần kết nối mạng và giúp Web site dễ phát triển hơn. Hãy xem xét việc nhà kinh doanh cần dữ liệu của khách hàng trong khi họ đi công tác. Khi bắt đầu ngày, họ sẽ cần đồng bộ dữ liệu với database chính  để lấy các thông tin cập nhật mới nhất. Trong suốt ngày hôm đó, họ sẽ thực hiện các thay đổi trên dữ liệu khách hàng hiện tại, thêm khách hàng mới, và nhập các hóa đơn mới. Điều này hợp lệ vì họ có nguồn dữ liệu khách hàng riêng và những người khác không thể thay đổi trên cùng dòng dữ liệu. Vào cuối ngày, nhà kinh doanh cập nhật sẽ kết nối vào mạng và cập nhật thay đổi cho tiến trình xử lý ban đêm.

Kịch bản khác là giúp Web site dễ phát triển hơn.Với một SqlDataReader, bạn phải trở lại database lấy dữ liệu mỗi khi xem một trang. Điều này yêu cầu một kết nối mới cho mỗi lần tải trang, nó sẽ ảnh hưởng lớn đến hiệu suất khi số lượng người dùng tăng lên. Một cách để khắc phục điều này là dùng DataSet, chỉ cần cập nhật một lần và lưu trong bộ nhớ tạm (cache). Mỗi yêu cầu tải trang sẽ kiểm tra cache và nạp dữ liệu (từ database) nếu nó không tồn tại hoặc lấy dữ liệu ra khỏi cache và hiển thị nó. Điều này giúp hạn chế truy xuất database và tăng hiệu suất cho ứng dụng của bạn.

Các trường hợp ngoại lệ cho kịch bản trên bao gồm các trường hợp bạn cần cập nhật dữ liệu. Bạn phải quyết định, dựa trên cách dữ liệu sẽ được dùng và chiến lược của bạn. Dùng disconnected data khi thông tin của bạn thường là read-only, nhưng hãy xem xét các giải pháp thay thế (như dùng đối tượng SqlCommand để cập nhật tức thời) khi bạn yêu cầu một vài thứ linh hoạt hơn. Cũng vậy, nếu số lượng dữ liệu quá lớn để lưu trong bộ nhớ, bạn sẽ cần dùng SqlDataReader để đọc dữ liệu.

Tạo đối tượng DataSet

Không có gì đặc biệt khi tạo một DataSet. Bạn chỉ cần tạo một thể hiện mới, giống bất kì đối tượng nào:

DataSet dsCustomers = new DataSet();

Constructor của DataSet không yêu cầu tham số. Tuy nhiên có một overload chấp nhận một chuỗi đại diện cho tên của DataSet, được dùng nếu bạn cần serialize dữ liệu thành XML. Bây giờ, ta có một DataSet rỗng và cần một SqlDataAdapter để nạp dữ liệu cho nó.

Tạo một SqlDataAdapter

SqlDataAdapter chứa các lệnh SQL và đối tượng connection để đọc và ghi dữ liệu. Bạn khởi tạo nó với câu SQL select và đối tượng connection:

SqlDataAdapter daCustomers = new SqlDataAdapter(“select CustomerID, CompanyName from Customers”, conn);

Dòng mã trên tạo một đối tượng SqlDataAdapter, daCustomers. Câu SQL select xác định dữ liệu nào sẽ được đọc vào DataSet. Đối tượng connection, conn, nên được khởi tạo từ trước, nhưng không được mở. Đó là công việc của SqlDataAdapter để mở và đóng connection khi phương thức Fill() và Update() được gọi.

SqlDataAdapter tất cả lệnh cần thiết để tương tác với dữ liệu nguồn. Dòng mã trên xác định câu lệnh select, nhưng không cho thấy câu lệnh insert, update và delete. Chúng sẽ được thêm vào SqlDataAdapter sau khi nó được khởi tạo.

Có hai cách để thêm các lệnh insert, update, delete: thông qua các property của SqlDataAdapter hoặc với một SqlCommandBuilder. Trong bài này, tôi sẽ cho bạn thấy cách dễ nhất để làm điều này với SqlCommandBuilder. Trong bài sau, tôi sẽ sử dụng các property của SqlDataAdapter, công việc này sẽ đòi hỏi nhiều bước hơn nhưng sẽ giúp bạn hiệu quả hơn cách làm việc của SqlCommandBuilder. Đây là cách để thêm các lệnh vào SqlDataAdapter với SqlCommandBuilder.

SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daCustomers);

Lưu ý rằng  dòng mã trên khởi tạo một đối tượng SqlCommandBuilder với constructor cần một tham số là SqlDataAdapter, daCustomers. Điều này giúp SqlCommandBuilder biết đối tượng SqlDataAdapter nào để thêm các lệnh vào. SqlCommandBuilder sẽ đọc câu SQL select (lấy từ SqlDataAdapter), từ đó suy ra các lệnh insert, update và delete, sau đó gán các lệnh mới vào các property Insert, Update, Delete của SqlDataAdapter tương ứng.

Như tôi đề cập từ trước, SqlCommandBuilder có hạn chế. Nó làm việc khi bạn làm một câu select đơn giản trên một bảng. Tuy nhiên, khi bạn cần kết hợp hai hoặc nhiều bảng hoặc thực thi một stored procedure, nó sẽ không làm việc.

Đổ dữ liệu vào DataSet

Để đổ dữ liệu vào DataSet bạn cần dùng phương thức Fill() của SqlDataAdapter, như sau:

daCustomers.Fill(dsCustomers, “Customers”);

Phương thức Fill(), trong dòng trên lấy hai tham số: một DataSet và một tên bảng.  DataSet phải được tạo trước khi bạn đổ dữ liệu vào nó. Tham số thứ hai là tên của bảng sẽ được tạo trong DataSet. Bạn có thể đặt bất kì tên gì cho bảng. Thông thường, tôi sẽ để tên bảng trùng với tên gốc của nó trong database. Tuy nhiên, nếu câu select của SqlDataAdapter chứa một lệnh join, bạn sẽ cần phải đặt một tên rõ ràng khác cho bảng.

Phương thức Fill() có một overload chấp nhận một tham số là DataSet. Trong trường hợp này, bảng được tạo sẽ có tên mặc định là “table1” cho bảng đầu tiên. Số này sẽ tăng dần (table2, tabl3,…,tableN) cho mỗi bảng thêm vào DataSet nếu như tên bảng không được chỉ ra trong phương thức Fill().

Sử dụng DataSet

Một DataSet sẽ gắn dữ liệu vào DataGrid của ASP.NET và Windows form. Đây là một ví dụ sẽ gán DataSet cho một Windows forms DataGrid:

dgCustomers.DataSource = dsCustomers;
dgCustomers.DataMember = “Customers”;

Điều đầu tiên chúng ta làm, trong đoạn mã trên, là gán DataSet cho property DataSource của DataGrid. Điều này giúp DataGrid biết nó có dữ liệu được gắn vào, tuy nhiên bạn sẽ thấy một dấu ‘+’ trên GUI bởi vì DataSet có thể giữ nhiều bảng và DataGrid cho phép bạn mở rộng ra để xem mỗi bảng trong đó. Để chỉ định bảng nào được dùng, gán property DataMember của DataGrid bằng tên của bảng. Trong ví dụ, chúng ta gán tên là Customers, là tên trùng với tên trong tham số thử hai trong phương thức Fill() của SqlDataAdapter. Đây là lý do tôi thích đặt tên bảng trong phương thức Fill(), và nó giúp đoạn mã dễ đọc hơn.

Cập nhật thay đổi

Sau khi thay đổi được thực hiện trên dữ liệu, bạn sẽ cần ghi lại vào database. Dòng mã sau cho thấy cách dùng phương thức Update của SqlDataAdapter để cập nhật các thay đổi vào database.

daCustomers.Update(dsCustomers, “Customers”);

Phương thức Update() trên được gọi trên thể hiện của SqlDataAdapter có tham số đầu tiên là chính đối tượng gọi phương thức. Tham số thử hai của phương thức Update() chỉ ra bảng nào trong DataSet sẽ được cập nhật. Bảng chứa một danh sách các dòng dữ liệu đã bị thay đổi và các property Insert, Update, Delete của SqlDataAdapter chứa các lệnh SQL dùng để thực hiện thay đối database.

Kết hợp tất cả lại

Tới bây giờ, bạn đã thấy các phần cần thiết để quản lý disconnected data. Cái bạn cần là xem tất cả công việc trong một ứng dụng. Listing 1 cho thấy cách đoạn mã từ các phần trước được dùng trong một chương trình hoàn chỉnh:

Listing 1: Implementing a Disconnected Data Management Strategy
using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;

class DisconnectedDataform : Form
{
	private SqlConnection  conn;
	private SqlDataAdapter daCustomers;

	private DataSet  dsCustomers;
	private DataGrid dgCustomers;

	private const string tableName = "Customers";

	// initialize form with DataGrid and Button
	public DisconnectedDataform()
	{
		// fill dataset
		Initdata();

		// set up datagrid
		dgCustomers = new DataGrid();
		dgCustomers.Location = new Point(5, 5);
		dgCustomers.Size = new Size(
			this.ClientRectangle.Size.Width - 10,
			this.ClientRectangle.Height - 50);
		dgCustomers.DataSource = dsCustomers;
		dgCustomers.DataMember = tableName;

		// create update button
		Button btnUpdate = new Button();
		btnUpdate.Text = "Update";
		btnUpdate.Location = new Point(
			this.ClientRectangle.Width/2 - btnUpdate.Width/2,
			this.ClientRectangle.Height - (btnUpdate.Height + 10));
		btnUpdate.Click += new EventHandler(btnUpdateClicked);

		// make sure controls appear on form
		Controls.AddRange(new Control[] { dgCustomers, btnUpdate });
	}

	// set up ADO.NET objects
	public void Initdata()
	{
		// instantiate the connection
		conn = new SqlConnection(
			"Server=(local);DataBase=Northwind;Integrated Security=SSPI");

		// 1. instantiate a new DataSet
		dsCustomers = new DataSet();

		// 2. init SqlDataAdapter with select command and connection
		daCustomers = new SqlDataAdapter(
			"select CustomerID, CompanyName from Customers", conn);

		// 3. fill in insert, update, and delete commands
		SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daCustomers);

		// 4. fill the dataset
		daCustomers.Fill(dsCustomers, tableName);
	}

	// Update button was clicked
	public void btnUpdateClicked(object sender, EventArgs e)
	{
		// write changes back to DataBase
		daCustomers.Update(dsCustomers, tableName);
	}

	// start the Windows form
	static void Main()
	{
		Application.Run(new DisconnectedDataForm());
	}
}

Phương thức Initdata() trong Listing 1 chứa các phương thức cần thiết để thiết lập SqlDataAdapter và DataSet. Lưu ý rằng các đối tượng dữ liệu khác nhau được định nghĩa tại cấp lớp vì thế chúng có thể được dùng trong nhiều phương thức. Property DataSource của DataGrid được gán trong constructor. Khi người dùng click nút Update, phương thức Update() trong sự kiện btnUpdateClickedsẽ được gọi, cập nhật thay đổi vào lại database.

Tổng kết

DataSet chứa nhiều bảng và có thể lưu trong bộ nhớ để tái sử dụng. SqlDataAdapter cho phép bạn đổ dữ liệu vào một DataSet và cập nhật thay đổi vào database. Bạn không cần lo lắng về việc mở và đóng SqlConnection bởi vì SqlDataAdapter tự động làm việc đó. Một SqlCommandBuilder tạo ra các câu lệnh insert, update, delete dựa trên câu select của SqlDataAdapter. Dùng phương thức Fill() của SqlDataAdapter để đổ dữ liệu vào DataSet. Gọi phương thức Update() của SqlDataAdapter để cập nhật thay đổi vào lại database.

Tôi hi vọng bạn thích bài này và mời bạn trở lại cho bài kết tiếp trong series này. Lesson 06: Thêm Parameter vào SqlCommand.

 

———–

Nguồn tham khảo: http://www.csharp-station.com/Tutorials/AdoDotNet/lesson05.aspx

https://yinyangit.wordpress.com

Advertisements

40 thoughts on “[ADO.NET Tutorial] Lesson 05: Làm việc với Disconnected Data – DataSet và SqlDataAdapter

  1. Chào bạn!
    Khi tôi xóa nhiều dòng dữ liệu trên Datagridview, ví dụ xóa 5 dòng dữ liệu, sau đó lại sửa dữ liệu ở dòng đầu tiên sau khi xóa và cuối cùng mới update xuống CSDL thì gặp 1 thông báo lỗi. Bạn gợi ý giúp tôi vấn đề này nha! Thanks!

    Phản hồi
  2. Thông tin bạn cung cấp chưa đầy đủ nên mình chưa thể xác định được vấn đề của bạn. Có một cách giải quyết chung là bạn lấy dòng thông báo lỗi và tìm trên google. Đa số đều có thể tìm được lời giải đáp phù hợp.

    Phản hồi
  3. Để xóa được nhiều dòng liên tiếp trên datagrid t đã dùng databinding để tự động thay đổi dữ liệu của datatable theo datagrid. Ví dụ nếu người dùng thực hiện liên tiếp 3 thao tác xóa, sửa, xóa ở dòng dữ liệu đầu tiên trên datagrid thì khi update xuống CSDL thì gặp báo lỗi. Lỗi này theo t hiểu là khi mình sửa dữ liệu ở dòng đầu tiên thì dòng đầu tiên sẽ được đánh dấu là updated, khi mình tiếp tục thực hiện xóa dữ liệu ở dòng đầu tiên thì sẽ lại update lại dữ liệu của datatable theo datagrid và đánh dấu dòng này là deleted. Vì thế nên khi update xuống CSDL thì dòng đầu tiên trên datatable sẽ được đánh dấu là updated và deleted nên sẽ xuất hiện thông báo lỗi.

    Phản hồi
  4. Sử dụng databinding cho control, bạn có thể bắt các sự kiện thay đổi từ DataGrid hoặc BindingSource để gọi phương thức cập nhật vào database. Bạn cũng có thể thử dùng phương thức AcceptChanges() của DataTable hoặc DataRow trước khi thực hiện thay đổi mới. Như vậy các trạng thái được đánh dấu của DataRow sẽ trở về mặc định là unchanged.

    Phản hồi
  5. Hi Yin Yang

    Mình có làm demo cho trường hợp này với 2 table có quan hệ 1 – nhiều, trường hợp xóa dữ liệu thì không có vấn đề gì nhưng với trường hợp Thêm mới & Cập nhật thì lại xảy ra 1 vấn đề ở table con, nếu table con có nhiều hơn 1 record thì có data của 01 column của record thứ 2 hoặc thứ 3 không được đưa vào CSDL (tự động gán NULL vì mình cho phép NULL ở column này), trên giao diện ngay khi thực hiện xong vẫn thể hiện giá trị nhập vào đầy đủ nhưng trong CSDL 01 column của record thứ 2 hoặc thứ 3 không được ghi vào đó. Tất cả các thao tác này mình thực hiện đồng thời trên 2 table. Bạn Yin Yang có biết nguyên nhân vì sao ko? Bạn có thể giúp mình khắc phục nó được ko?

    Phản hồi
  6. Yin Yang :

    Bạn thử kiểm tra lại code và debug xem có chèn đầy đủ vào lệnh insert chưa? Nếu cột đó chỉ là cột dữ liệu thông thường thì có thể ko liên quan đến relationship.

    Hi Yin Yang

    Mình debug rồi, xem chi tiết bên trong SqlDataAdapter.Update(ds,tablename); thì thấy nó tạo chuỗi Insert đúng. Mình đang thắc mắc có phải chăng kiểu dữ liệu trong CSDL column đó mình định nghĩa là money trong khi dữ liệu lấy trên gridView control của DevExpress có thể là string nên trong lúc debug không nhìn thấy Size & Value được truyền vào, vậy mà nó vẫn Insert vào CSDL chỉ duy nhất 01 column của 01 hàng (trường hợp có nhiều hơn 01 hàng như đã nói ở comment trên ) bị NULL. Có nghĩa là nó chỉ insert được 01 hàng trọn vẹn trong bất kỳ trường hợp nào, còn trường hợp có nhiều hơn 01 hàng thì column có kiểu dữ liệu money lại bị NULL. Không biết phải sửa lại sao cho đúng nữa Yin Yang ui!

    Đây là code để xử lý vấn đề này: Mình code ở event of button Update

    SqlConnection cnn = new SqlConnection(GlobalVariable.WSInfo.ConnectionString);

    SqlDataAdapter sqlda1 = new SqlDataAdapter(“SELECT * FROM KT1”, cnn);
    SqlCommandBuilder cmdBldr1 = new SqlCommandBuilder(sqlda1);

    SqlDataAdapter sqlda2 = new SqlDataAdapter(“SELECT * FROM KT2”, cnn);
    SqlCommandBuilder cmdBldr2 = new SqlCommandBuilder(sqlda2);

    DataTable dt; // data changed
    // Case: Delete
    dt = ds.Tables[“KT2”].GetChanges(DataRowState.Deleted);
    if (dt != null) sqlda2.Update(ds, “KT2”);
    dt = ds.Tables[“KT1”].GetChanges(DataRowState.Deleted);
    if (dt != null) sqlda1.Update(ds, “KT1”);

    // Case: Add new
    dt = ds.Tables[“KT1”].GetChanges(DataRowState.Added);
    if (dt != null) sqlda1.Update(ds, “KT1”);
    dt = ds.Tables[“KT2”].GetChanges(DataRowState.Added);
    if (dt != null) sqlda2.Update(ds, “KT2”);

    // Case: Modify
    dt = ds.Tables[“KT1”].GetChanges(DataRowState.Modified);
    if (dt != null) sqlda1.Update(ds, “KT1”);
    dt = ds.Tables[“KT2”].GetChanges(DataRowState.Modified);
    if (dt != null) sqlda2.Update(ds, “KT2”);

    Yin Yang xem có giúp được mình không nha!!! Thanks so much!

    Phản hồi
  7. Hi Yin Yang

    Bạn có thể giải thích rõ hơn cho mình hiểu về vài dòng code này được ko?

    // 2. init SqlDataAdapter with select command and connection
    daCustomers = new SqlDataAdapter(“select CustomerID, CompanyName from Customers”, conn);
    // 3. fill in insert, update, and delete commands
    SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daCustomers);
    // 4. fill the dataset
    daCustomers.Fill(dsCustomers, tableName);

    // write changes back to DataBase
    daCustomers.Update(dsCustomers, tableName);

    Tôi ko thấy việc truyền các dữ liệu cần thiết vào để có thể thực hiện Insert, Update hay Delete. Việc truyền câu lệnh select ở trên có ý nghĩa gì? Trong khi tôi hiều điều đó có nghĩa là ta đang muốn lấy data của 2 columns (CustomerID, CompanyName). Làm sao để biết được khi nào ta thực thi Insert, Update hay Delete?

    Thanks!!!

    Phản hồi
  8. – Như tên gọi của nó, dựa vào câu select được truyền vào, đối tượng SqlCommandBuilder sẽ tự động tạo ra các câu lệnh insert, delete, update.

    – Dữ liệu được update ở đây chính là đối tượng dsCustomers, một DataAdapter có thể cập nhật database từ một DataTable hoặc DataSet. Tức là mọi thay đổi bạn thực hiện trong DataSet hay DataTable sẽ được cập nhật vào trong database qua phương thức Update().

    – Thay vì tạo và gọi các câu lệnh insert, delete, update thì bạn nên thay đổi dữ liệu trên DataTable.

    Phản hồi
  9. Hi Yin Yang

    Mình nêu lại ý tưởng bài toán hôm trước post bên bài DataSet Part 1 nha, giờ đưa qua đây thấy hợp với chủ đề này hơn.

    Mình có 2 tables ở CSDL01 có relation 1 – nhiều, tạm gọi là table A (cha), table B (con). Bài toán yêu cầu lấy data có ở A & B với điều kiện thời gian do user chọn – Fill lên gridView, sau đó nếu user nhấn Insert sẽ đưa về 2 tables C & D của CSDL2, data của A đưa vào C, data của B đưa vào D (dĩ nhiên C & D có relation 1 – nhiều, C là cha, D là con & A – C có tên column tương tự nhau, B – D cũng vậy).

    Mình đang vướng ở chỗ Insert data ngược trở lại C & D, bởi vì khi dữ liệu được Fill vào DataSet.Tables[“C”] & DataSet.Tables[“D”] sẽ có tình trạng là 1 cha nhiều con hoặc 1 cha 1 con.

    Ví dụ: Khi Fill vào dataset table C có 2 record mang ID là 01, 02 & table D có 3 record nhận ID 01 1 record, nhận ID 02 2 record. table C có 2 record mang ID là 01, 02 & table D có 4 record nhận ID 01 2 record, nhận ID 02 2 record…..

    Đồng thời khi Insert tôi phải thay đổi ID khóa chính của A qua lại cho C là 01 string tự tăng, ví dụ: ABC@0001 nếu có trong C rùi thì tăng lên ABC@0002 … (cái này ko có gì để nói)

    Cái khó ở đây là làm sao để Insert đồng thời đúng thứ tự data với nhau giống như tôi ví dụ bên trên ấy.

    Yin Yang tham mưu giúp tôi xem làm cách nào để phân biệt chính xác thứ tự của chúng.

    Phản hồi
  10. Nói lại cho rõ chỗ ví dụ, đó là ở ví dụ trên mới chỉ có 2 trường hợp cho 1 mốc thời gian được User chọn thôi nha Yin Yang, bài toán của mình có data khá lớn, có thể 100, 1000…record ở 1 mốc thời gian khi user chọn, số liệu thay đổi theo thời gian luôn. Ví dụ: ngày hôm nay có 100 record, ngày mai 200 record ….nhưng điều có mối quan hệ như đã nói ở trên.

    Hy vọng Yin Yang giúp được !!!! Thanks so much!!!!

    Phản hồi
  11. Nếu là như thế, bạn chỉ cần filldata cho table C trước, sau đó mới fill cho table B. Việc dữ liệu theo thứ tự nào phụ thuộc vào cách sắp xếp dữ liệu của bạn, thông thường là sẽ sắp xếp theo ID. Còn về phần thời gian thì mình nghĩ ko có gì phức tạp nếu như các table đã có sẵn một cột lưu thời gian.

    Phản hồi
  12. YinYang cho mình hỏi là mình có 2 bảng
    KhachHang(MaKH,,TenKH,DiaChi)
    ChiTietDatHang(MaDatHang,MaKH,NgayDatHang)
    Nó có quan hệ qua khóa MaKH mình muốn là khi mình có thay đổi gì ở bảng KhachHang như sửa,xóa thì MaKH ở bàng ChiTietDatHang cũng tự động thay đồi theo.Cảm ơn bạn nhé

    Phản hồi
  13. Hi Yin Yang
    Bạn cho hỏi mình muốn thêm SqlTransaction vào cách dùng Disconnected Data với DataSet & SqlDataAdapter thì phải code ntn cho đúng để khi thực hiện thêm, xóa, sửa nếu có lỗi chương trình tự động RollBack. Bạn demo code giúp nhé! Thanks!!!

    Phản hồi
  14. Hi Yin Yang

    Link bạn cung cấp mình cũng đã search tìm hiểu rùi nhưng nó ko đúng với câu hỏi của mình rùi. Mình copy lại câu hỏi nha.
    “Bạn cho hỏi mình muốn thêm SqlTransaction vào cách dùng Disconnected Data với DataSet & SqlDataAdapter thì phải code ntn cho đúng để khi thực hiện thêm, xóa, sửa nếu có lỗi chương trình tự động RollBack. Bạn demo code giúp nhé! Thanks!!!”

    Phản hồi
  15. Bạn cho mình hỏi là mình có 2 DateTimeInput txtNgayDatHang,txtNgayGiaoHang để nhập ngày tháng năm,
    trong CSDL mình có 2 field ngày đặt hàng ,ngày giao hàng set kiểu DateTime
    Trong Properties của 2 cái trên mình set Format là Custom,Custom Format là dd/MM/yy
    Đoạn lệnh mình Insert vào
    Chi_Tiết_Đặt_Hàng.ChiTietDatHangPersistance.InsertData(txtMaKH.Text, txtSoLuongBinh21L.Text, txtSoLuongBinh1_5L.Text, txtSoLuongBinh0_5L.Text, txtNgayDatHang.Text, txtNgayGiaoHang.Text);
    Nhưng khi insert thì nó bào lỗi
    String was not recognized as a Valid DateTime
    Thanks!

    Phản hồi
  16. Trong cái hàm InsertData thì mình chỉ truyền các đối số vào thôi,còn bên trong nó thì nó sẽ thực hiện Convert và insert vào database,nếu mình sửa thành txtNgayDatHang.value.ToShortDateString(),txtNgayGiaoHang.value.ToShortDateString(),thí nó Insert đc nhưng mà nó không theo định dạng mình muốn là dd/mm/yyyy mà nó theo mm/dd/yyyy

    Phản hồi
  17. à mình đọc không kĩ tưởng bạn xài textbox, nếu đã có được giá trị datetime rồi thì khi chèn bạn hãy dùng property Value chứ ko nên xài kiểu string. Khi đã có giá trị DateTIme rồi thì việc hiển thị ra sao không quan trọng vì đã có sẵn các phương thức format rồi.

    Phản hồi
  18. mình cũng thực hiện thao tác dữ lịêu bằng dataset tuy nhiên gặp 1 lỗi là khi Update thì database lại báo lỗi là “Database is locked” mình tìm hiểu rất nhiều topic khác nhưng vẫn chưa khắc phục đc lỗi này … hi vọng bạn có thể giúp mình tháo gỡ 🙂

    Phản hồi
  19. Pingback: [ADO.NET Tutorial] Lesson 04: Đọc dữ liệu với SqlDataReader | fishgold192

  20. Yin Yang cho mình hỏi có cách nào trong C# đối với datatable cho kết quả giống câu SQL:
    SELECT * FROM table WHERE field1 in (SELECT field1 FROM table WHERE field2 like *ABC*)
    Mình làm được trong phần “()” như table.select(“field2 like *ABC*”), còn sử dụng kết quả này cho câu select trước mình không biết làm. Thanks

    Phản hồi

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s