ADO.NET-Cơ bản về DataSet: DataRelation và Constraint

DataSet là một đối tượng có thể chứa nhiều DataTable cùng với mối liên hệ giữa chúng (relationship) và kể các ràng buộc (constraint) được lưu hoàn toàn trong bộ nhớ để làm việc offline. Qua bài viết này, bạn có thể hiểu cấu trúc của DataSet, DataTable cũng như nạp dữ liệu, tạo relation, constraint và thao tác dữ liệu trên các đối tượng dữ liệu này.

Bài viết trước: Tìm hiểu về cấu trúc của DataSet và DataTable trong ADO.NET

Data Relation trong DataSet

Tạo DataRelation:

Khi bạn thêm các table vào DataSet thì giữa chúng chưa có relation nào. Để tạo ra một relation, bạn sử dụng property Relations của DataSet để thêm vào các đối tượng DataRelation. Ví dụ sau tạo ra một relation giữa hai table Group, User trong DataSet thông qua cột GroupID trong mỗi table với tên relation mà tôi đặt là Group_User.

DataSet dataSet = LoadData();

DataTable userTable = dataSet.Tables["User"];

DataTable groupTable = dataSet.Tables["Group"];

DataRelation relation=new DataRelation("Group_User",
        groupTable.Columns["GroupID"],
        userTable.Columns["GroupID"]);

dataSet.Relations.Add(relation);

Phương thức GetChildRows():

Sau khi có relation, ta có thể dùng phương thức instance DataRow.GetChildRows() để lấy về một mảng các DataRow trong bảng con của bảng hiện tại.

DataRow[] groupRows = groupTable.Select("GroupID='1'");

DataRow[] memberRows = groupRows[0].GetChildRows("Group_User");

foreach (var row in memberRows)
    Console.WriteLine(row["UserName"]);

Output:

Adon
Balrog
Bison
Cammy
ChunLi
Dan
DeeJay

Dòng lệnh đầu tiên của đoạn mã trên lấy về một mảng DataRow bằng phương thức DataTable.Select(string filterExpression) với tham số là một câu lệnh lọc (giống biểu thức sau từ khóa “where” trong SQL).

Dựa vào dữ liệu, ta biết mảng này thực chất chỉ chứa một phần tử, chính vì thế ta lấy phần tử đầu tiên của mảng groupRows và gọi phương thức GetChildRows(string relationName). Và kết quả in ra màn hình sẽ là những user nằm trong GroupID là 1, tức là “Member”.

Phương thức GetParentRow():

Ngược với GetChildRows(), phương thức GetParentRow() trả về một DataRow từ bảng cha của bảng hiện tại dựa vào relation giữa chúng. Ví dụ sau cho thấy GroupName của user có UserID là “8”:

DataRow[] childRows = userTable.Select("UserID='8'");

DataRow parentRow = childRows[0].GetParentRow("Group_User");

Console.WriteLine(parentRow["GroupName"]);

Output:

Admin

Primary Key trong DataTable
DataTable có thể dùng một hoặc nhiều DataColumn để tạo ra một Primary Key. Primary Key là định danh phân biệt các DataRow và tránh trùng lặp dữ liệu. Dựa vào PrimaryKey, bạn mới có thể dùng phương thức Find() của DataRowCollection.

Đoạn code bên dưới sẽ tìm và trả về dòng dữ liệu với UserID là “1” trong table User:

DataColumn[] primaryKeys=new DataColumn[] {  table.Columns["UserID"] };

table.PrimaryKey = primaryKeys;

DataRow row = table.Rows.Find("1");

Console.WriteLine(row["UserName"]);

// Ouput: Adon

Data Constraint trong DataTable

Constraint là các “luật lệ” mà bạn có thể đặt cho DataColumn nhằm hạn chế và đảm bảo một vài quy tắc nào đó. Có hai loại constraint mà bạn có thể sử dụng:

–       UniqueConstraint: Các giá trị của cột phải là unique (duy nhất).

–       ForeignKeyConstraint: Duy trì liên kết giữa các DataTable trong DataSet.

Hai lớp này đều thừa kế từ lớp abstract Constraint:

UniqueConstraint:

DataTable userTable = dataSet.Tables["User"];
Constraint constraint=new UniqueConstraint(userTable.Columns["UserID"],true);
userTable.Constraints.Add(constraint);

Ví dụ trên sẽ tạo constraint cho cột UserID của table User. Tham số thứ hai của constructor UniqueConstraint chỉ ra cột được sử dụng có phải là primary key không. Vì tôi đặt là true, cột UserID này sẽ trở thành primary key của table này. Bạn có thể đặt thêm UniqueConstraint này cho bất kì cột nào muốn bảo đảm giữ liệu không trùng nhau.

Việc đặt constraint như trên tương đương với việc bạn gán property DataColumn.Unique = true. Và với tham số true để xác định primary key thì tương đương với việc bạn đặt DataTable.PrimaryKey như trong phần trên.

Để xem DataTable có các constraint nào, bạn chỉ cần lặp qua các phần tử trong collection DataTable.Constraint.

ForeignKeyConstraint

Constraint này được dùng để tạo relation giữa hai cột thuộc hai table (tạo foreign key cho bảng).  ForeignKeyConstraint phải được thêm vào table con vì đây là table chứa foreign key

. Ví dụ ta tạo constraint này cho cột GroupID của hai table Group và User:

DataColumn parent = dataSet.Tables["Group"].Columns["GroupID"];
DataColumn child = dataSet.Tables["User"].Columns["GroupID"];

ForeignKeyConstraint constraint = new ForeignKeyConstraint("FK_Group_User", parent, child);

constraint.UpdateRule = Rule.Cascade;
constraint.DeleteRule = Rule.SetNull;

dataSet.Tables["User"].Constraints.Add(constraint);

Sau khi đặt constraint này, nếu thử thêm một user có GroupID không nằm trong table Group, bạn sẽ nhận được một exception “InvalidConstraintException”

dataSet.Tables[“User”].Rows.Add(13, “Test”, 999);

InvalidConstraintException: ForeignKeyConstraint FK_Group_User requires the child key values (99) to exist in the parent table.

Một điểm chú ý là constraint này không tương đương với việc bạn đặt relation trong DataSet. Vì vậy bạn không thể dùng các phương thức GetChildRows(), GetParentRow().

Enum Rule

Enum này có bốn giá trị mà bạn có thể sử dụng, theo mô tả dưới đây.

Rule settingDescription
CascadeDelete or update related rows.
SetNullSet values in related rows to DBNull.
SetDefaultSet values in related rows to the default value.
NoneTake no action on related rows. This is the default.

Như ví dụ trên, thuộc tính UpdateRule của constraint được đặt mặc định là Rule.Cascade để nếu có thay đổi từ table cha, các dòng tương ứng trong table con sẽ tự động cập nhật lại. Hoặc các dòng dữ liệu tương ứng trong table con sẽ bị xóa nếu như table cha xóa một dòng dữ liệu.

Bạn thử sửa GroupID đầu tiên của table Group từ 1 thành 9. Sau đó kiểm tra lại table User xem các dòng có GroupID ban đầu là 1 có chuyển thành 9 không:

dataSet.Tables["Group"].Rows[0]["GroupID"] = 9;

foreach(DataRow row in dataSet.Tables["User"].Select("GroupID='9'"))
Console.WriteLine(row["UserName"]);

Output:

Adon
Balrog
Bison
Cammy
ChunLi
Dan
DeeJay

Luật thứ hai DeleteRule = Rule.SetNull để quy định rằng nếu dữ liệu bên table cha bị xóa, các dòng tương ứng trong table con sẽ được gán bằng DBNull.