Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции_CSharp_4.docx
Скачиваний:
37
Добавлен:
02.11.2018
Размер:
237.61 Кб
Скачать

4.10. Схема данных и типизированные dataset

Описав в предыдущих параграфах основные компоненты набора данных, рассмотрим класс DataSet более подробно. Начнем с таблицы свойств класса.

Таблица 11

Свойства класса DataSet

Имя свойства

Описание

CaseSensitive

Определяет, учитывается ли регистр при поиске строк

DataSetName

Строка с именем набора данных

EnforceConstraints

Определяет, обеспечивает ли DataSet выполнение определенных на нем ограничений

ExtendedProperties

Коллекция пользовательских свойств набора данных

HasErrors

Указывает, содержит ли набор данных ошибки

Locale

Свойство имеет тип CultureInfo и определяет региональные параметры, используемые набором данных при сравнении строк

Relations

Коллекция отношений, связывающих таблицы из набора данных

RemotingFormat

Позволяет указать формат данных при сериализации DataSet – бинарный или XML

Tables

Возвращает коллекцию таблиц набора данных

Большинство свойств в особых пояснениях не нуждается, ибо, по сути, является аналогом свойств таблицы, но для всего набора данных. Свойство Relations содержит коллекцию связей между таблицами и является часть описания схемы данных. Если значение свойства EnforceConstraints установить в false, то при загрузке информации в DataSet данные не будут проверяться на соответствие ограничениям. Это повышает производительность.

В табл. 12 перечислены методы набора данных:

Таблица 12

Методы класса DataSet

Имя метода

Описание

AcceptChanges()

Метод фиксирует все изменения данных, которые были проделаны с момента предыдущего вызова AcceptChanges()

Clear()

Уничтожаются все строки всех таблиц набора данных

Clone()

Метод клонирует структуру набора и возвращает пустой набор

Copy()

Метод клонирует и структуру, и данные набора

GetChanges()

Возвращает новый DataSet с идентичной схемой, содержащий измененные строки и таблицы оригинального объекта DataSet

GetXml()

Возвращает содержимое объекта DataSet в виде XML-строки

GetXmlSchema()

Возвращает схему объекта DataSet в виде XML-строки

HasChanges()

Возвращает логическое значение, указывающее, содержат ли строки из состава DataSet отложенные изменения

Merge()

Осуществляет слияние данных из другого объекта DataSet, DataTable или массива объектов DataRow и данных текущего объекта DataSet

ReadXml()

Читает содержимое DataSet в XML-формате из файла, Stream, TextReader или XmlReader

ReadXmlSchema()

Работает как ReadXml(), но читает только схему DataSet

RejectChanges()

Метод отменяет изменения, которые еще не зафиксированы вызовом AcceptChanges()

Reset()

Восстанавливает оригинальное состояние DataSet

WriteXml()

Записывает содержимое DataSet в XML-формате в файл, Stream, TextWriter или XmlWriter

WriteXmlSchema()

Работает как WriteXml(), но записывает только схему DataSet

Рассмотрим, как вручную задать схему набора данных. Напомним, что правильная схема включает тип и имя отдельных столбцов таблицы, ограничения на столбцы и связи между таблицами. Будем создавать схему для таблиц Artists и Disks.

Вначале создадим объекты, соответствующие столбцам и таблицам, и поместим столбцы в таблицы:

var Artists = new DataTable("Artists");

var id_artists = new DataColumn

{

ColumnName = "id",

DataType = typeof(int),

AllowDBNull = false

};

var name = new DataColumn

{

ColumnName = "name",

DataType = typeof(string),

AllowDBNull = false

};

Artists.Columns.Add(id_artists);

Artists.Columns.Add(name);

var Disks = new DataTable("Disks");

var id_disks = new DataColumn

{

ColumnName = "id",

DataType = typeof(int),

AllowDBNull = false

};

var title = new DataColumn

{

ColumnName = "title",

DataType = typeof(string),

MaxLength = 50,

AllowDBNull = false

};

var artist_id = new DataColumn("artist_id", typeof(int));

var release_year = new DataColumn

{

ColumnName = "release_year",

DataType = typeof(string),

MaxLength = 4

};

Disks.Columns.Add(id_disks);

Disks.Columns.Add(title);

Disks.Columns.Add(artist_id);

Disks.Columns.Add(release_year);

Перейдем к работе с таблицами. Как и в реляционных база данных, один или несколько столбцов таблицы DataTable могут исполнять роль первичного ключа. Первичный ключ должен быть уникальным в пределах таблицы. Свойство таблицы PrimaryKey служит для получения или установки массива столбцов, формирующих первичный ключ. Если столбец является частью первичного ключа, его свойство AllowDBNull автоматически устанавливается в false. Установим первичные ключи наших таблиц:

Artists.PrimaryKey = new[] { id_artists };

Disks.PrimaryKey = new[] { id_disks };

Таблица поддерживает свойство Constraints – набор ограничений таблицы. Значением данного свойства является коллекция объектов класса Constraint. Класс Constraint – абстрактный базовый класс, имеющий два производных класса: ForeignKeyConstraint и UniqueConstraint.

Класс UniqueConstraint – это класс, при помощи объектов которого реализуется концепция уникальности значений полей строки. Основными свойствами этого класса является массив столбцов Columns, имя ограничения ConstraintName и булево свойство IsPrimaryKey, которое показывает, представляет ли данное ограничение первичный ключ таблицы. Ограничение вида UniqueConstraint автоматически добавляется в таблицу при создании первичного ключа. Это же ограничение появляется, если в таблицу добавляется столбец с установленным свойством Unique.

Так как в нашем примере мы уже создали первичные ключи таблиц, то их коллекции Constraints не пусты. Изменим имя ограничения в одной из таблиц и добавим ограничение для столбца name таблицы Artists, полагая значения в этом столбце уникальными:

Artists.Constraints[0].ConstraintName = "Primary Key";

var uc = new UniqueConstraint("Unique Name", new[] { name });

Artists.Constraints.Add(uc);

Класс ForeignKeyConstraint служит для описания внешних ключей таблицы. Его основные свойства перечислены в табл. 13.

Таблица 13

Основные свойства класса ForeignKeyConstraint

Имя свойства

Описание

AcceptRejectRule

Определяет, каскадируются ли результаты вызова методов AcceptChanges() и RejectChanges() родительского объекта DataRow в дочерние строки

Columns

Столбцы дочерней таблицы, составляющие ограничение

ConstraintName

Имя ограничения

DeleteRule

Определяет, каскадируются ли удаление родительского объекта DataRow в дочерние строки

ExtendedProperties

Набор динамических свойств

RelatedColumns

Столбцы родительской таблицы, составляющие ограничение

RelatedTable

Родительская таблица ограничения

Table

Дочерняя таблица ограничения

UpdateRule

Управляет каскадированием изменений родительской строки в дочерние строки

Свойства AcceptRejectRule, DeleteRule и UpdateRule управляют порядком каскадирования изменений родительской строки в дочерние строки. Свойство AcceptRejectRule принимает значение из одноименного перечисления. Значение этого свойства по умолчанию – None: вызов метода AcceptChanges() или RejectChanges() объекта DataRow не сказывается на дочерних строках последнего. Если задать свойству AcceptRejectRule значение Cascade, изменения каскадируются в дочерние строки, определенные объектом ForeignKeyConstraint.

Свойства DeleteRule и UpdateRule функционируют аналогичным образом, но принимают значения из перечисления Rule. Значение этих свойств по умолчанию – Cascade, т. е. изменения родительской строки каскадируются в дочерние строки. Например, при вызове метода Delete() родительского объекта DataRow вы неявно вызываете и метод Delete() его дочерних строк. Точно так же, редактируя значение поля ключа родительского объекта DataRow, вы неявно изменяете содержимое соответствующих полей дочерних строк. Если каскадировать изменения не требуется, задайте свойствам DeleteRule и UpdateRule значение None. Можно также задать им значение SetNull или SetDefault. В первом случае при изменении или удалении содержимого родительской строки соответствующим полям дочерних строк задаются значения NULL, а во втором – их значения по умолчанию.

Обычно нет необходимости создавать объекты класса ForeignKeyConstraint вручную. Они генерируются автоматически при добавлении связи между таблицами. Связь между таблицами представлена объектом класса DataRelation. Большинство свойств DataRelation доступно только для чтения. Задать их значение можно средствами конструкторов объекта DataRelation. В табл. 14 перечислены наиболее часто используемые свойства.

Таблица 14

Свойства класса DataRelation

Имя свойства

Описание

ChildColumns

Свойство возвращает массив, содержащий объекты DataColumn из дочернего объекта DataTable

ChildKeyConstraint

Указывает ограничение FOREIGN KEY в дочерней таблице

ChildTable

Указывает дочернюю таблицу связи

DataSet

Набор данных, в котором находится объект DataRelation

ExtendedProperties

Набор динамических свойств

Nested

Логическое значение. Указывает, нужно ли преобразовывать дочерние строки в дочерние элементы при записи содержимого DataSet в XML-файл

ParentColumns

Родительские столбцы, определяющие отношение

ParentKeyConstraint

Указывает ограничение UNIQUE в родительской таблице

ParentTable

Указывает родительскую таблицу

RelationName

Строка с именем отношения

При создании объекта DataRelation следует указать его имя, чтобы объект удалось найти в наборе; кроме этого, необходимо указать родительский и дочерний столбцы, на которых будет основано отношение. Чтобы упростить создание связей, класс DataRelation предоставляет отдельные конструкторы, принимающие как объекты DataColumn, так и массивы таких объектов.

Вернемся к нашему примеру. Создадим рассоединенный набор данных и поместим в него таблицы:

var CD_Rent = new DataSet("CD_Rent");

CD_Rent.Tables.Add(Artists);

CD_Rent.Tables.Add(Disks);

Между таблицами Artists и Disks существует связь по внешнему ключу. А именно, таблица Disks является дочерней для таблицы Artists, так как значения поля artist_id – это значения первичного ключа таблицы Artists. Создадим объект DataRelation, описывающий эту связь:

// используем простой конструктор, который устанавливает

// имя отношения, родительский и дочерний столбцы отношения

var Artists_to_Disks = new DataRelation("Artists_to_Disks",

id_artists, artist_id);

// добавим отношение в набор данных

CD_Rent.Relations.Add(Artists_to_Disks);

После выполнения данного кода коллекция Constraints объекта Artists будет содержать два отношения с именами Primary Key и Unique Artist Name (оба – класса UniqueConstraint). Коллекция Constraints объекта Disks будет также содержать два отношения. Одно – с именем Constraint1 типа UniqueConstraint, второе – с именем Artists_to_Disks типа ForeignKeyConstraint. Создание схемы данных можно считать завершенным. При желании можно добавить обработчики событий в наши таблицы.

При помощи метода WriteXmlSchema() созданную схему можно сохранить в XML-файле (правильным расширеним файла является *.xsd, так как схема сохраняется в виде XSD-описания).

Платформа .NET поддерживает типизированные наборы данных. Типизированный набор данных – это класс, который является наследником класса DataSet. В отличие от обычного DataSet, в типизированном наборе для доступа к отдельным компонентам служат свойства. Имя свойства – это имя элемента DataSet (например, имя таблицы); тип свойства – класс-наследник стандартного класса из DataSet.

Создать типизированный DataSet можно несколькими способами. В Visual Studio для этого используется специальный мастер (wizard). В состав .NET Framework SDK входит утилита xsd.exe, формирующая типизированные наборы данных на основании XSD-файла со схемой набора.

Если использовать утилиту xsd.exe для схемы из предыдущих примеров, получим класс CD_Rent, обладающий типизированными свойствами для таблиц, и классы ArtistsDataTable и DisksDataTable, наследуемые от DataTable. Теперь работать с набором данных можно следующим образом. Вместо кода, подобного следующему,

var s = (string)CD_Rent.Tables["Artists"].Rows[0]["name"];

можно писать такой код:

var s = CD_Rent.Artists[0].name;

Обратите внимание:

  1. Таблица представлена свойством Artists.

  2. Для доступа к определенной строке таблицы используется индексатор, примененный к свойству Artists.

  3. Тип строки – ArtistsRow. Это позволяет обратиться к полю строки как к свойству и не выполнять приведение типов.

Подытожим: типизированные DataSet позволяют установить контроль правильности кода во время компиляции. Это способно упростить разработку приложений и сделать использование типов более безопасным.