MSDN Documentation

Microsoft Developer Network

DataTable Constraints

Constraints are rules that enforce data integrity within a DataTable. They help ensure that the data stored in the table is accurate, consistent, and adheres to specific business logic. ADO.NET provides support for two primary types of constraints:

Unique Constraints

A UniqueConstraint ensures that the values in one or more columns are unique across all rows in the DataTable. This is similar to a primary key in a relational database, but a DataTable can have multiple unique constraints.

You can create a UniqueConstraint in the following ways:

Example: Implicit Unique Constraint


DataTable customersTable = new DataTable("Customers");
DataColumn idColumn = new DataColumn("CustomerID", typeof(int));
idColumn.Unique = true; // Implicit Unique Constraint
customersTable.Columns.Add(idColumn);
customersTable.Columns.Add("Name", typeof(string));

// Adding rows that violate the constraint will throw an exception
try
{
    customersTable.Rows.Add(1, "Alice");
    customersTable.Rows.Add(1, "Bob"); // This will throw UniqueConstraintException
}
catch (ConstraintException ex)
{
    Console.WriteLine($"Error adding row: {ex.Message}");
}
            

Example: Explicit Unique Constraint on Multiple Columns


DataTable orderItemsTable = new DataTable("OrderItems");
orderItemsTable.Columns.Add("OrderID", typeof(int));
orderItemsTable.Columns.Add("ProductID", typeof(int));
orderItemsTable.Columns.Add("Quantity", typeof(int));

// Enforce uniqueness on OrderID and ProductID combined
UniqueConstraint orderProductKey = new UniqueConstraint("OrderProductKey", new DataColumn[] { orderItemsTable.Columns["OrderID"], orderItemsTable.Columns["ProductID"] }, true);
orderItemsTable.Constraints.Add(orderProductKey);

try
{
    orderItemsTable.Rows.Add(101, 501, 2);
    orderItemsTable.Rows.Add(101, 502, 1);
    orderItemsTable.Rows.Add(101, 501, 3); // This will throw UniqueConstraintException
}
catch (ConstraintException ex)
{
    Console.WriteLine($"Error adding row: {ex.Message}");
}
            

Foreign Key Constraints

A ForeignKeyConstraint enforces referential integrity between two DataTable objects. It ensures that values in a column (or set of columns) in a "child" table must match values in a column (or set of columns) in a "parent" table. This prevents orphaned records.

When creating a ForeignKeyConstraint, you specify:

Example: Foreign Key Constraint


// Parent Table: Products
DataTable productsTable = new DataTable("Products");
productsTable.Columns.Add("ProductID", typeof(int));
productsTable.Columns.Add("ProductName", typeof(string));
productsTable.PrimaryKey = new DataColumn[] { productsTable.Columns["ProductID"] }; // Primary key for parent

// Child Table: OrderItems
DataTable orderItemsTable = new DataTable("OrderItems");
orderItemsTable.Columns.Add("OrderID", typeof(int));
orderItemsTable.Columns.Add("ProductID", typeof(int));
orderItemsTable.Columns.Add("Quantity", typeof(int));

// Add data to parent table
productsTable.Rows.Add(501, "Laptop");
productsTable.Rows.Add(502, "Mouse");

// Create ForeignKeyConstraint
ForeignKeyConstraint fk_OrderItems_Products = new ForeignKeyConstraint("FK_OrderItems_Products", productsTable.Columns["ProductID"], orderItemsTable.Columns["ProductID"]);
fk_OrderItems_Products.DeleteRule = Rule.Cascade; // If product is deleted, delete related order items
fk_OrderItems_Products.UpdateRule = Rule.Cascade; // If ProductID is updated, update related order items

orderItemsTable.Constraints.Add(fk_OrderItems_Products);

// Add data to child table
orderItemsTable.Rows.Add(101, 501, 1); // Valid: ProductID 501 exists in Products
orderItemsTable.Rows.Add(102, 502, 2); // Valid: ProductID 502 exists in Products

// Attempt to add an item with a non-existent ProductID
try
{
    orderItemsTable.Rows.Add(103, 999, 1); // Invalid: ProductID 999 does not exist
}
catch (ConstraintException ex)
{
    Console.WriteLine($"Error adding order item: {ex.Message}");
}

// Example of DeleteRule.Cascade
// productsTable.Rows.Find(501).Delete();
// Note: You need to call AcceptChanges() on the related tables for the delete to propagate fully.
            

Constraint Actions (Rules)

When defining a ForeignKeyConstraint, you specify rules for handling operations on the parent table that would affect the child table. These rules are defined by the Rule enumeration:

Working with Constraints

The Constraints collection of a DataTable holds all the constraints associated with that table.

You can iterate through the constraints or access them by name:


foreach (Constraint constraint in myDataTable.Constraints)
{
    Console.WriteLine($"Constraint Name: {constraint.ConstraintName}, Type: {constraint.GetType().Name}");
}

UniqueConstraint uc = (UniqueConstraint)myDataTable.Constraints["MyUniqueConstraintName"];
            

Constraints are crucial for maintaining data quality and consistency when working with DataTable objects, especially when synchronizing data with a database or performing complex data manipulations.