Технология Microsoft ADO.NET

       

Программное создание объектов


Еще в первой лекции мы узнали, что все объекты вкладки 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-схемами, пока про это название (до лекции 10) мы можем просто забыть.

Создадим теперь объект 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)


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

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


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

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


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

Полностью2) метод 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).


Содержание раздела