Foreign Key, Unique Constraint в DataSet

Все исходники / Язык программирования C# / OS Windows / Базы данных - Database / SQL Server / Foreign Key, Unique Constraint в DataSet
Оглавление:
  1. Ограничения в DataSet
  2. Что такое Foreign Key Constraint
  3. Foreign Key Constraint
  4. Правило Unique Constraint
  5. Полный код метода создания ограничений
  6. Пример создания ограничений

Ограничения в DataSet

Модальное окно о нарушении ограничений

Ограничения Foreign Key, Unique Constraints предназначены для исключения ошибок при вводе связанных и уникальных значений в поля таблицы. Правила ограничений существенно облегчают обслуживание и модернизацию баз данных.

В объект класса DataSet ограничения и связи не загружаются из базы данных. Ограничения необходимо настраивать вручную. Ограничения Foreign Key автоматически создаются при установлении отношений между таблицами, ограничения Unique определяются программным способом.

В прикрепленном приложении пример организации кода работы с определением правил ограничений. Нарушение любого ограничения в приложении вызывает модальное окно с предупреждением и указанием ячейки места нарушения.

Что такое Foreign Key Constraint

Foreign Key Constraint означает правило ограничения внешнего ключа, предназначенное для создания рациональных баз данных путём определения первичного значения в родительской таблице ссылочно связанного с внешними ключами дочерних таблиц.

Лучше всего понять смысл данного ограничения можно на примере: есть таблица с описанием товаров Product, где есть поле TypeId определяющее к какому типу принадлежит товар; есть таблица Type описания типов товаров, имеющая поля с уникальными значениями Id. Логически, поле Product.TypeId должно быть одним из значений Type.Id.

Не имея никаких правил ограничения, требуется серьезное напряжение внимания при вводе значений в поля Product.TypeId, чтобы не допустить расхождения со значениями полей Type.Id. Кроме того, после ввода "правильных" значений Product.TypeId, первичные значения Type.Id можно намеренно или ошибочно изменить (или вовсе удалить), что неизбежно нарушит логику отношений товаров и типов товаров.

При установке ограничения внешнего ключа (первичный ключ - Type.Id, внешний ключ - Product.TypeId), невозможно сохранение данных с "неправильными" значениями Product.TypeId. Значения внешнего ключа строго определяет первичный ключ. А при изменении первичного ключа автоматически изменяются значения соответствующих внешних ключей.

Foreign Key Constraint

При создании связей между таблицами DataSet, автоматически создаются ограничения Foreign Key Constraint (с возможностью отмены в случае необходимости) для установления правил изменения значений первичного ключа в родительской таблице. Правила устанавливаются свойствами ForeignKeyConstraint.DeleteRule и ForeignKeyConstraint.UpdateRule. Данные свойства имеют тип enum Rule с полями определяющие правила:

  • Cascade - дочерние строки обновляются или удаляются одновременно с соответствующими родительскими строками. Установленно по умолчанию.
  • None - невозможно обновить и удалить родительское значение пока имеются строки в дочерней таблице с этим значением.
  • SetDefault - при обновлении или удалении родительского значения дочерним задаются значения по умолчанию, содержащиеся в свойстве DefaultValue.
  • SetNull - при обновлении или удалении родительского значения, внешний ключ в дочерней таблице устанавливается в DBNull.
Программный код автоматического создания ограничения Foreign Key Constraint и настройка правил удаления первичного значения:
void SetRelationConstraint(DataSet ds)
{
    // Определение пары связанных таблиц.
    // Вместо имён таблиц применены константы для облегчения
    // одновременного изменения названий таблиц во всём решении.
    DataTable parentTable = ds.Tables[Constants.ParentTableName];
    DataTable childTable = ds.Tables[Constants.ChildTableName];

    // При определении связей между столбцами таблиц автоматически создаётся
    // ограничение внешнего ключа. Название ограничения необходимо
    // для перемещения между связанными таблицами.
    DataColumn parentColumn = parentTable.Columns["Id"];
    DataColumn childColumn = childTable.Columns["TypeId"];
    DataRelation relation = new("FK_Product_Type", parentColumn, childColumn, true);
    ds.Relations.Add(relation);

    // Настройка правила удаления значения первичного ключа 
    // для ограничения с определенным именем.
    // В данном случае устанавливается запрет удаления первичного значения
    // при существующих внешних ключах.
    ForeignKeyConstraint foreignKeyConstraint = 
        (ForeignKeyConstraint)childTable.Constraints["FK_Product_Type"];
    foreignKeyConstraint.DeleteRule = Rule.None;
    . . .
}

Правило Unique Constraint

Unique Constraint - это правило ограничения ввода повторяющихся значений в поля определенного столбца таблицы, все значения полей данного столбца должны быть уникальные.

Кроме запрета ввода одинаковых значений в поля одного столбца, ограничение Unique обеспечивает ограничение ввода одинакового набора значений в группу столбцов. Например, столбец TypeId и SKUType ограничивают ввод одинаковой пары значений в их поля.

Программный код определения уникального ограничения на один столбец и на пару столбцов. Правило Unique Constraint вводится в пределах дочерней таблицы. Полный код метода создания отношений и ограничений. Unique Constraint создаётся на уникальность имени типа продукта в таблице Type. Пара столбцов в таблице продуктов Product: артикул типа Product.SKUType и тип продукта Product.TypeId требуют уникальный набор значений.

void SetRelationConstraint(DataSet ds)
{
    ...
    
    // Значения установленного столбца таблицы должны быть уникальными 
    // в пределах соответствующей таблицы.
    UniqueConstraint uniqueName = new(parentTable.Columns["Name"]);
    parentTable.Constraints.Add(uniqueName);
    UniqueConstraint uniqueSKU = new(childTable.Columns["SKU"]);
    childTable.Constraints.Add(uniqueSKU);

    // Ограничение на ввод пары одинаковых значений в поля данных столбцов.
    UniqueConstraint uniqueTwoColumns = 
        new(new DataColumn[]{ childTable.Columns["TypeId"], childTable.Columns["SKUType"] });
    childTable.Constraints.Add(uniqueTwoColumns);
    ...
}

Полный код метода создания ограничений

Метод предназначен для автоматического создания правил Foreign Key Constraint после установления связей между столбцами пары таблиц. Поскольку Unique Constraint не создаётся автоматически правило определяется вручную.

void SetRelationConstraint(DataSet ds)
{
    // Таблицы участвующие в отношениях.
    DataTable parentTable = ds.Tables[Constants.ParentTableName];
    DataTable childTable = ds.Tables[Constants.ChildTableName];

    // Программное определение отношений между таблицами.
    DataColumn parentColumn = parentTable.Columns["Id"];
    DataColumn childColumn = childTable.Columns["TypeId"];
    DataRelation relation = new("FK_Product_Type", parentColumn, childColumn, true);
    ds.Relations.Add(relation);

    // Определение правил реакции на удаление первичного значения.
    ForeignKeyConstraint foreignKeyConstraint = 
        (ForeignKeyConstraint)childTable.Constraints["FK_Product_Type"];
    foreignKeyConstraint.DeleteRule = Rule.None; // Rule.Cascade; Rule.SetDefault; Rule.SetNull

    // Значения указанных столбцов таблиц будут уникальными 
    // в пределах своей таблицы.
    UniqueConstraint uniqueName = new(parentTable.Columns["Name"]);
    parentTable.Constraints.Add(uniqueName);
    UniqueConstraint uniqueSKU = new(childTable.Columns["SKU"]);
    childTable.Constraints.Add(uniqueSKU);

    //  Набор из значений двух столбцов будет иметь уникальное сочетание.
    UniqueConstraint uniqueTwoColumns = 
        new(new DataColumn[]{ childTable.Columns["TypeId"], childTable.Columns["SKUType"] });
    childTable.Constraints.Add(uniqueTwoColumns);


    // Установка свойства автонумерации идентификаторов.
    // Если заполнение DataSet построить на DataTable эта строка не потребуется.
    DataColumn columnId = parentColumn;
    columnId.AutoIncrement = true;
    columnId.ReadOnly = true;
    DataColumn columnId2 = childTable.Columns["Id"];
    columnId2.AutoIncrement = true;
    columnId2.ReadOnly = true;

    // Вручную устанавливаем запрет NULL.
    // Если заполнение DataSet построить на DataTable эта строка не потребуется.
    parentTable.Columns["Name"].AllowDBNull = false;
    childTable.Columns["SKU"].AllowDBNull = false;
}

Пример создания ограничений

Прикрепленный исходник содержит программный код примера создания правил Foreign Key, Unique Constraints. Скрипт создания базы данных можно скопировать на странице Relations Tables в DataSet. База данных не имеет установленных ограничений, кроме первичных ключей. Все ограничения создаются программно. Программа создана в MS Visual Studio 2022.