Опубликован: 05.08.2007 | Уровень: специалист | Доступ: платный
Лекция 8:

Объекты DataTable, DataRow и DataColumn

Программное создание объектов DataTable и DataColumn. Ограничения Unique Constraint и ForeignConstraint

Еще в "Элементы работы с базами данных" мы узнали, что все объекты вкладки Data или все объекты ADO .NET можно создавать программно. Скопируйте папку приложения Tests и назовите ее "ProgrammTests". Открываем проект, удаляем объект DataSet с панели компонент формы. Для создания этого объекта программно достаточно следующей строчки кода:

DataSet dsTests = new DataSet();

Поскольку созданный экземпляр dsTests будет использоваться в нескольких методах, эту строку следует написать в классе формы. Здесь dsTests - это название объекта DataSet, которое мы затем используем в коде - это известно нам с "Элементы работы с базами данных" . Среда Visual Studio .NET однако, сгенерировала следующий фрагмент кода (его можно найти в исходном проекте "Tests"):

this.dsTests = new System.Data.DataSet();
...
// 
// dsTests
// 
this.dsTests.DataSetName = "NewDataSet"; 
this.dsTests.Locale = new System.Globalization.CultureInfo("ru-RU");

Как всегда, автоматический код избыточен - здесь указывается культура ru-RU. Дело в том, что у меня установлена русская версия Windows XP и соответствующие региональные настройки.

Среда определила еще свойство DataSetName = "NewDataSet", эквивалентное описание будет иметь следующий вид:

DataSet dsTests = new DataSet("NewDataSet");

Что же это за еще одно название объекта DataSet? Свойство DataSetName используется для работы с XSD-схемами, пока про это название (до "Введение в XML" ) мы можем просто забыть.

Создадим теперь объект DataTable для таблицы Questions:

DataTable dtQuestions = dsTests.Tables.Add("Questions");
//Или
//DataTable dtQuestions = new DataTable("Questions");
//dsTests.Tables.Add(dtQuestions);

Здесь мы создаем экземпляр dtQuestions объекта DataTable, затем вызываем метод Add свойства Tables объекта dsTests, которому передаем название таблицы Questions. Далее создаем поля в объекте dtQuestions:

DataColumn dсQuestID = dtQuestions.Columns.Add("questID", typeof(Int32));
dсQuestID.Unique = true;
DataColumn dcQuestion = dtQuestions.Columns.Add("question");
DataColumn dcQuestType = dtQuestions.Columns.Add("questType", typeof(Int32));

Мы создаем поля, нужные для отражения соответствующих столбцов в таблице Questions. Перегруженный метод Add свойства Columns объекта dtQuestions позволяет задавать название столбца и его тип данных (рис. 8.7):

Создание поля

увеличить изображение
Рис. 8.7. Создание поля

Свойство Unique указывает, что в этом поле не должно быть повторяющихся значений, оно должно быть уникальным (здесь - поле questID является первичным ключом таблицы Questions).

Точно так же создаем объект DataTable для таблицы Variants и соответствующие поля:

//Cоздаем таблицу "Variants"
DataTable dtVariants = dsTests.Tables.Add("Variants");
	
//Заполняем поля таблицы "Variants"
DataColumn dcID = dtVariants.Columns.Add("id", typeof(Int32));
dcID.Unique = true;
dcID.AutoIncrement = true;
DataColumn dcVariantQuestID = dtVariants.Columns.Add("questID", typeof(Int32));
DataColumn dcVariant = dtVariants.Columns.Add("variant");
DataColumn dcIsRight = dtVariants.Columns.Add("isRight", typeof(Boolean));

Здесь мы дополнительно установили свойству AutoIncrement объекта dcID значение true. Свойство AutoIncrement (Счетчик) позволяет создать счетчик для поля, аналогичный типу данных "Счетчик" в Microsoft Access.

Теперь приступим к созданию связи между таблицами. В базе данных Microsoft SQL Tests между родительской таблицей Questions и дочерней Variants была установлена связь по полю questID, которое было в обеих таблицах. При программном создании объектов для поля questID таблицы Questions был создан объект dсQuestID, для этого же поля таблицы Variants создан объект dcVariantQuestID. В коде создание отношения между таблицами будет иметь следующий вид:

DataRelation drQuestionsVariants = new DataRelation("QuestionsVariants",
 dсQuestID, dcVariantQuestID);
dsTests.Relations.Add(drQuestionsVariants);

Здесь в конструкторе drQuestionsVariants - название экземпляра объекта (класса) DataRelation, а QuestionsVariants - свойство relationName - название связи, которая будет содержаться в объекте drQuestionsVariants. Другими словами, drQuestionsVariants - название экземпляра DataRelation, которое мы будем использовать в коде, а свойство relationName - всего лишь название отражаемой связи, которую можно удалить или переименовать.

Итак, мы создали все объекты для отображения таблиц, полей и даже связей между таблицами.

Теперь нам осталось определить некоторые свойства таблиц, называемые ограничениями. Свойство ограничения ( Constraint ) объекта DataTable бывает двух типов - UniqueConstraint и ForeignKeyConstraint. Свойство UniqueConstraint определяет первичный ключ таблицы, например, в таблице Questions ключевым полем является questID. Объект dсQuestID представляет это поле:

DataColumn dсQuestID = dtQuestions.Columns.Add("questID", typeof(Int32));

Ограничение UniqueConstraint, налагаемое на объект dсQuestID, запрещает появление дублированных строк:

UniqueConstraint UC_dtQuestions = new UniqueConstraint(dсQuestID);
dtQuestions.Constraints.Add(UC_dtQuestions);

Однако при создании объекта dсQuestID мы ведь уже определяли его уникальность:

dсQuestID.Unique = true;

Действительно, последняя строка представляет собой неявный способ задания ограничения UniqueConstraint. Если мы уже определили уникальное поле или поля, используя свойство Unique, задавать ограничение UniqueConstraint не нужно.

Второе ограничение - ForeignKeyConstraint - определяет, как должны себя вести дочерние записи при изменении родительских записей и наоборот. Конечно, интерфейс нашего приложения вообще не подразумевает внесение изменений, но ADO .NET требует точного описания объектов для управления ими. Ограничение ForeignKeyConstraint содержит следующие три правила:

  • UpdateRule - применяется при изменении родительской строки;
  • DeleteRule - применяется при удалении родительской строки;
  • AcceptRejectRule - применяется при вызове метода AcceptChanges объекта DataTable, для которого определено ограничение.

Для этих правил могут применяться следующие значения:

  • Cascade - каскадное обновление связанных записей;
  • None - изменения в родительской таблице не отражаются в дочерних записях;
  • SetDefault - полю внешнего ключа в дочерних записях присваивается значение, заданное в свойстве DefaultValue этого поля;
  • SetNull - полю внешнего ключа в дочерних записях присваивается значение Null.

Значением по умолчанию для правил UpdateRule и DeleteRule является Cascade, для правила AcceptRejectRule - None. Дополнительно, правило AcceptRejectRule принимает значения только Cascade или None.

Создадим ограничение для связи QuestionsVariants:

ForeignKeyConstraint FK_QuestionsVariants = 
 new ForeignKeyConstraint(dtQuestions.Columns["questID"],
 dtVariants.Columns["questID"]);
dtVariants.Constraints.Add(FK_QuestionsVariants);

Здесь задается вначале родительская колонка, а затем дочерняя (рис. 8.8). Добавлять созданное ограничение следует к объекту DataTable, представляющему дочернюю таблицу (в данном случае - объект dtVariants )

Создание ограничения FK_QuestionsVariants

увеличить изображение
Рис. 8.8. Создание ограничения FK_QuestionsVariants

Этот фрагмент кода оставляет значения правил UpdateRule, DeleteRule и AcceptRejectRule заданными по умолчанию, т.е. Cascade и None, что соответствует значениям настройки с помощью мастера Relation (рис. 8.9):

Мастер Relation и соответствующий фрагмент кода

увеличить изображение
Рис. 8.9. Мастер Relation и соответствующий фрагмент кода

Если бы нам потребовалось задать значение одному из правил, отличное по умолчанию, мы бы просто применили другой вариант конструктора (рис. 8.10):

 В этом конструкторе можно задать значения правил RejectRule, DeleteRule и UpdateRule

увеличить изображение
Рис. 8.10. В этом конструкторе можно задать значения правил RejectRule, DeleteRule и UpdateRule

Полностью2Не забудьте в классе формы создать объект DataSet: DataSet dsTests = new DataSet(); метод LoadDataBase в проекте ProgrammTests будет выглядеть так:

private void LoadDataBase()
{
	SqlConnection conn = new SqlConnection("Data Source=.;Initial
	 Catalog=Tests;Integrated Security=SSPI;");
	SqlDataAdapter questAdapter =
	 new SqlDataAdapter("select * from questions", conn);
	SqlDataAdapter variantsAdapter =
	 new SqlDataAdapter("select * from variants", conn);
	//dsTests.EnforceConstraints = true;
	//Cоздаем таблицу "Questions"
	DataTable dtQuestions = dsTests.Tables.Add("Questions");
	//Или
	//DataTable dtQuestions = new DataTable("Questions");
	//dsTests.Tables.Add(dtQuestions);
	//Заполняем поля таблицы "Questions"
	DataColumn dсQuestID =
	 dtQuestions.Columns.Add("questID", typeof(Int32));
	dсQuestID.Unique = true;
	//Или
	//UniqueConstraint UC_dtQuestions = new UniqueConstraint(dсQuestID);
	//dtQuestions.Constraints.Add(UC_dtQuestions);
	DataColumn dcQuestion = dtQuestions.Columns.Add("question");
	DataColumn dcQuestType =
	 dtQuestions.Columns.Add ("questType", typeof(Int32));
	//Cоздаем таблицу "Variants"
	DataTable dtVariants = dsTests.Tables.Add("Variants");
	//Заполняем поля таблицы "Variants"
	DataColumn dcID = dtVariants.Columns.Add("id", typeof(Int32));
	dcID.Unique = true;
	dcID.AutoIncrement = true;
	DataColumn dcVariantQuestID =
	 dtVariants.Columns.Add("questID", typeof(Int32));
	DataColumn dcVariant = dtVariants.Columns.Add("variant");
	DataColumn dcIsRight = dtVariants.Columns.Add("isRight", typeof(Boolean));
	//Создаем ограничение
	ForeignKeyConstraint FK_QuestionsVariants = 
	 new ForeignKeyConstraint(dtQuestions.Columns["questID"],
	 dtVariants.Columns["questID"]);
	dtVariants.Constraints.Add(FK_QuestionsVariants);
	//Создаем отношение
	DataRelation drQuestionsVariants = new DataRelation("QuestionsVariants",
	 dсQuestID, dcVariantQuestID);
	dsTests.Relations.Add(drQuestionsVariants);
	conn.Open();
	//Заполняем таблицу "Questions" данными из questAdapter
	questAdapter.Fill(dsTests.Tables["Questions"]);
	//Заполняем таблицу "Variants" данными из variantsAdapter
	variantsAdapter.Fill(dsTests.Tables["Variants"]);
	conn.Close();
}

Обратим внимание на несколько деталей этого метода. Значение true свойства EnforceConstraints объекта dsTests разрешает использование ограничений. По умолчанию в созданном объекте DataSet это свойство и так принимает значение true, поэтому этот фрагмент кода закомментирован. Следует иметь в виду, что для снятия всех ограничений достаточно установить свойству EnforceConstraints значение false. Ограничение FK_QuestionsVariants создается перед отношением drQuestionsVariants - вначале следует создавать ограничения, а затем определять отношения. Соединение conn открывается как можно позже - непосредственно перед заполнением данными объектов DataAdapter, - и тут же закрывается. Открыть его в начале метода и закрыть в конце было бы нерациональным.

В программном обеспечении к курсу вы найдете приложение Programm Tests (Code\Glava4\ProgrammTests).

Александра Тимофеева
Александра Тимофеева
Украина, Киев
Bakke Aleksander
Bakke Aleksander
Россия, Mуниципальный округ N 4