Rambler's Top100
Главная
Новости
Статьи
Форумы
Книги
Коды
Сообщество
Блоги
О нас
 

Логин

Email:
  Пароль:

Войти
Зарегистрироваться
Забыл пароль

Поиск

 Искать :
 
Вперед

Работа с данными вчера, сегодня, завтра. Схема в Typed dataset.

Работа с данными вчера, сегодня, завтра. Схема в Typed dataset.

Автор: Serg Vorontsov aka V©R©N  (voron@aspnetmania.com)
Опубликовано: 11 August 2002
Уровень: Учебник

Версия для печати

Проголосовало 33 читателей
Средняя оценка 4.61

Введение.

Итак, наконец-то переходим к третьей части. К статье, посвещенной теме: "Создание схем типизированных датасетов". В предыдущих, статьях мы достаточно подробно коснулись деталей, т.е. иными словами, "внутренностей" типизированных датасетов, ну а теперь пришло время рассмотреть систему в целом.

И что же оно такое - Dataset? Это полноценная система из таблиц, строк, столбцов, ограничений (constraints) и отношений (relations). Датасет может содержать полный слепок БД, или же только ее фрагмент. Кроме того, датасет предоставляет возможность управления этими взаимосвязями не только в статике, но и в динамике.

Давайте пройдемся еще раз по архитектуре Dataset для более тонкого его восприятия. Что же мы имеем?

Вернемся опять ко «внутренностям». При создании типизированного датасета мы манипулируем следующими объектами/действиями. Перетаскивая таблицу из окна Server Explorer на форму мы получаем экземпляры классов connection и adapter. После чего, перетаскивая все нужные нам таблицы на форму, в итоге, получаем один класс connection и столько экземпляров классов adapter, сколько мы захотели иметь таблиц на нашей форме. После чего, нажав правую кнопку мыши, мы имеем возможность, сгенерировать, тот самый, пресловутый, типизированный датасет. После этого нажатия, система предлагает нам создать отдельный класс, название которого нам дружелюбно предложит ввести «мастер». Это будет наследник от стандартного класса Dataset. Внутри этого класса Вы сможете увидеть классы таблиц, наследников от DataTable, строк, наследников от DataRow и параметров событий, наследников от EventsArg. Получается, что в результате генерации мы получили по три класса на каждую таблицу.

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

  1. Генерация XSD при помощи IDE VS.Net средствами дизайнера DataSet.
  2. Генерация XSD в Run-Time на основе DataAdapter и Dataset.
  3. Генерация XSD с использованием wizard для «типизированных» dataset.
  4. Генерация XSD при помощи утилиты xsd.exe

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

Экскурс в Ado.Net DataSet.

Прежде всего это Класс, который позволяет описать мини-модель Вашего сервера БД со всей его аттрибутикой, такой как связи, ограничения и прочее. Но что самое главное, в новой модели .NET, программист имеет возможность получить всю эту модель в памяти, причем с возможностью отключения от основной БД, что дает программисту огромные возможности. Можно подключаться к БД только для синхронизации изменений и опять отключаться. А для того чтобы все изменения были корректно сохранены, предоставлен богатейший класс SqlException. На момент сохранения изменений можно получить фактически три набора данных, текущий, фактический и оригинальный. А это дает возможность вводить еще и логику приоритетов для сохранения. DataAdapter, совместно с Connection и Command образует так называемый middle уровень связи типизированного датасета на клиентской стороне с исходным источником данных на серверной стороне. Обобщенно говоря, в Ado.Net можно получить практически полный контроль над данными. И это сказано без преувеличения.

Экскурс в XML и XML Schema definition (XSD).

В технологии .NET впервые формат XML можно считать "родным" форматом, позволяющим хранить данные (XML) и схемы данных (XSD). Данные форматы позволяют манипулировать, управлять и передавать информацию о датасете. И не важно данные это или схема. Датасет имеет встроенные возможности по сериализации и десериализации данных и/или схемы. Также можно воспользоваться сериализацией экземпляров объектов при помощи стандартных методов.

Мало того, большая часть интерфейса IDE VS.Net, равно как и многие файлы проекта сохраняет свои интерфейсы и значения в XML.

Опустимся на землю с небес. Что же нам предлагает фотмат описания схемы XML Schema Definition? А предлагает она нам хранение правил бизнес логики, отношений между таблицами, типов полей, счетчики, группировки и прочее. Т.е. все то, что предлагают стандартные интерфейсы серьёзных серверных решений, таких как MS SQL Server.

Вообщем все прекрасно в этом, но есть маленькое «но»: В тот момент, когда создаётся типизированный датасет на основе созданных адаптеров, то, к великому огорчению, в нем отсутствуют связи (relations). А жаль. Я бы сказал даже, что очень жаль. Ведь одно дело, когда база данных состоит из пяти, шести таблиц – и совсем по другому стоит вопрос, когда их около 300.

Но об этом чуть позже.

А сейчас я предлагаю взглянуть на основные понятия схемы:

  • sсhema - элемент, объявляющий «корень» (root) в XSD файле.
  • element – элемент, собственно объявляет элемент
    • name – атрибут, обозначает имя элемента.
    • type – атрибут, который имеет одно из значений, либо это тип данных, либо простой тип либо комплексный.
  • complexType – элемент, объявляющий сложный (комплексный) тип.
  • choice – элемент, определяющий определенное правило исключительно для прямых наследников(childs) элементов.
    • maxOccurs – атрибут, определяет количество повторений для «child» элемента.
  • sequence - элемент, определяющий порядок появления для «child» элементов.
  • simpleType – элемент, самый простой тип, который содержит лишь текстовое значение.
    • restriction – "child" элемент, ограничитель на значение
      • maxLenght – атрибут, максимальная длинна на simpleType
  • unique – элемент, определяет уникальность для элемента.
    • name – атрибут, определяет имя уникального элемента
    • selectror – "child" элемент, определяющий XPath элемента и содржит атрибут либо «child» элемент чье имя будет показано а поле атрибута.
      • xpath – атрибут, содержит выражение для XPath.
    • field – "child" элемент, объявляющий имя аттрибута либо элемента, содержащегося в XPath selector.
  • key – элемент, определяет ключевой элемент или атрибут. Включает в себя “parent” элемент.
  • keyref – элемент, определяет ссылку из атрибута или элемента на key либо unique элемент.

Способов по созданию схемы (xsd) действительно множество. Создавать их можно программно, визуально или при помощи утилит. Причем классов, которые будут в этом участвовать, действительно множество. Connection, Adapter, Command, DataRelations, Constraints и собственно сам DataSet – вот ключевые узлы, которые будут принимать участие в построении объекта под названием Typed Dataset.

Ручное создание схемы Dataset и вывод ее в файл схемы (XSD).

Я предлагаю перейти к созданию первого примера. Для этого нам есть смысл создать новый проект. Давайте назовем его «FirstTyped» (хоть и не очень оригинально, но зато по существу :)). Причем пускай проект будет консольный.

Итак, что мы имеем:

После создания проекта мы должны сделать следующее: создать субмодель (или схему) нашего будущего типизированного датасета. Я предлагаю в очередной раз «потрепать» нашу БД NorthWind. Давайте создадим новую диаграмму. Для этого, после того, как Вы сгенерировали новый проект, откройте окно Server Explorer и установите «выбор» на БД NorthWind -> DatabaseDiagram:

В разделе «Database Diagram», нажмите правую клавишу мыши, создайте новую диаграмму и добавтьте в нее таблицы Orders и Employees:

После того как Вы это сделаете, «мастер» создаст для Вас новую диаграмму. Выглядеть она должна следующим образом:

IDE VS.Net позволяет Вам сохранять эти диаграммы на сервере точно так же, как и файлы проекта. Если Вы нажмете на кнопку «сохранить», с изображением дискетки, Вам будет предложено выбрать название, под которым будет сохранена диаграмма, но в нашем случае это не столь важно. Более важный момент это то, что нам бы хотелось видеть типы и размерность полей для наших таблиц. Дизайнер позволяет сделать и это. Нажмите правую клавишу мышки на таблице и выберите отображение “Standart“ - в этом случае вы будете видеть всю информацию о таблице.

Проделайте это для обеих таблиц и в результате Вы должны получить следующее отображение в редакторе диаграмм:

Теперь я предлагаю начать с самого начала, написать небольшой кусочек кода, который бы отражал наш небольшой Dataset. Это будет хорошая практика в преддверии того, чтобы понять, что же будет потом делать «мастер».

После генерации «мастером» нашего консольного приложения, вносим небольшие коррективы руками:

Создаем экземпляр таблицы «Employees» предварительно добавив несколько using-ов для олегчения написание нам строк кода:

using System;
using System.Data;
using System.Data.SqlClient;


namespace FirstTyped
{
	/// 
	/// Summary description for Class1.
	/// 
	class FirstTypedClass
	{
		/// 
		/// The main entry point for the application.
		/// 
		[STAThread]
		static void Main(string[] args)
		{
			DataTable dtEmployees = new DataTable("Employees");
		}
	}
}
Создав экземпляр таблицы, мы, по логике, должны создать экземпляры колонок. И это действительно так. Давайте займемся и этим.

static void Main(string[] args)
{
	DataTable dtEmployees = new DataTable("Employees");

	DataColumn dcEmployeeID = new DataColumn("EmployeeID", System.Type.GetType("System.Int32"));
	//AllowDBNull = false; потому что поле Primary Key и не может быть null
	dcEmployeeID.AllowDBNull = false;
	//AutoIncrement = true; потому что поле со значением счетчика.
	dcEmployeeID.AutoIncrement = true;
	//ReadOnly = true; потому что поле не может изменяться на стороне клиента
	dcEmployeeID.ReadOnly = true;
	//Unique = true; Так как должно содержать уникальные значения и мы должны быть в этом уверены
	dcEmployeeID.Unique = true;
}

Что и для чего в данном коде – я написал в комментариях. Теперь мы должны проделать те же операции и для остальных таблиц и колонок нашего «маленького» dataset.

Пишем.... Пишем...... Пишем......... То и дело, подглядывая в нашу диаграмму, чтобы видеть тип и размерность, и не ошибиться.

Н-да. Я, кажется, понял, чем я буду наказывать провинившихся молодых программистов: написанием датасетов «вручную». Я даже всерьез задумался, нет ли у меня случайно склонности к «дедовщине».:)

Я такой работой никогда не занимался, и мне даже самому стало интересно, сколько времени у меня на это уйдёт. На набор всех типов колонок, казалось бы, простого набора таблиц, у меня ушло минут 40, с перерывом на кофе :).

...
static void Main(string[] args)
{
	DataTable dtEmployees = new DataTable("Employees");

	DataColumn dcEmployeeID = new DataColumn("EmployeeID", typeof(int));
	//AllowDBNull = false; потому что поле Primary Key и не может быть null
	dcEmployeeID.AllowDBNull = false;
	//AutoIncrement = true; потому что поле со значением счетчка.
	dcEmployeeID.AutoIncrement = true;
	//ReadOnly = true; потому что поле не может изменяться на стороне клиента
	dcEmployeeID.ReadOnly = true;
	//Unique = true; Потому что должно содержать уникальные значения и мы должны быть в этом уверенны
	dcEmployeeID.Unique = true;

	DataColumn dcLastName = new DataColumn("LastName", typeof(string));
	//AllowDBNull = false; потому что данное поле по диаграме не может содержать null
	dcLastName.AllowDBNull = false;
	// собственно размерность по диаграмме
	dcLastName.MaxLength = 20;

	DataColumn dcFirstName = new DataColumn("FirstName", typeof(string));
	//AllowDBNull = false; потому что данное поле по диаграме не может содержать null
	dcFirstName.AllowDBNull = false;
	// собственно размерность по диаграмме
	dcLastName.MaxLength = 10;

	DataColumn dcTitle = new DataColumn("Title", typeof(string));
	// собственно размерность по диаграмме
	dcTitle.MaxLength = 30;

	DataColumn dcTitleOfCourtesy = new DataColumn("TitleOfCourtesy", typeof(string));
	// собственно размерность по диаграмме
	dcTitleOfCourtesy.MaxLength = 25;

	DataColumn dcBirthDate = new DataColumn("BirthDate", typeof(DateTime));

	DataColumn dcHireDate = new DataColumn("HireDate", typeof(DateTime));

	DataColumn dcAddress = new DataColumn("Address", typeof(string));
	// собственно размерность по диаграмме
	dcAddress.MaxLength = 60;

	DataColumn dcCity = new DataColumn("City", typeof(string));
	// собственно размерность по диаграмме
	dcCity.MaxLength = 15;

	DataColumn dcRegion = new DataColumn("Region", typeof(string));
	// собственно размерность по диаграмме
	dcRegion.MaxLength = 15;

	DataColumn dcPostalCode = new DataColumn("PostalCode", typeof(string));
	// собственно размерность по диаграмме
	dcPostalCode.MaxLength = 10;

	DataColumn dcCountry = new DataColumn("Country", typeof(string));
	// собственно размерность по диаграмме
	dcCountry.MaxLength = 15;

	DataColumn dcHomePhone = new DataColumn("HomePhone", typeof(string));
	// собственно размерность по диаграмме
	dcHomePhone.MaxLength = 24;

	DataColumn dcExtension = new DataColumn("Extension", typeof(string));
	// собственно размерность по диаграмме
	dcExtension.MaxLength = 4;

	DataColumn dcPhoto = new DataColumn("Photo", typeof(byte[]));

	DataColumn dcNotes = new DataColumn("Notes", typeof(string));

	DataColumn dcReportsTo = new DataColumn("ReportsTo",typeof(int));

	DataColumn dcPhotoPath = new DataColumn("PhotoPath", typeof(string));
	// собственно размерность по диаграмме
	dcPhotoPath.MaxLength = 255;

	//-------------- Создаем таблицу Orders с описанием колонок -----------------------
	DataTable dtOrders = new DataTable("Orders");

	DataColumn dcOrderID = new DataColumn("OrderID", typeof(int));
	//AllowDBNull = false; потому что поле Primary Key и не может быть null
	dcOrderID.AllowDBNull = false;
	//AutoIncrement = true; потому что поле со значением счетчка.
	dcOrderID.AutoIncrement = true;
	//ReadOnly = true; потому что поле не может изменяться на стороне клиента
	dcOrderID.ReadOnly = true;
	//Unique = true; Потому что должно содержать уникальные значения и мы должны быть в этом уверенны
	dcOrderID.Unique = true;

	DataColumn dcCustomerID = new DataColumn("CustomerID", typeof(string));
	// собственно размерность по диаграмме
	dcCustomerID.MaxLength = 5;

	DataColumn dcEmployeeIDOrd = new DataColumn("EmployeeID", typeof(int));
	DataColumn dcOrderDate = new DataColumn("TitleOfCourtesy", typeof(System.DateTime));
	DataColumn dcRequiredDate = new DataColumn("RequiredDate", typeof(System.DateTime));
	DataColumn dcShippedDate = new DataColumn("ShippedDate", typeof(System.DateTime));
	DataColumn dcShipVia = new DataColumn("ShipVia", typeof(int));
	DataColumn dcFreight = new DataColumn("Freight", typeof(System.Decimal));
	DataColumn dcShipName = new DataColumn("ShipName", typeof(string));
	// собственно размерность по диаграмме
	dcShipName.MaxLength = 40;

	DataColumn dcShipAddress = new DataColumn("ShipAddress", typeof(string));
	// собственно размерность по диаграмме
	dcShipAddress.MaxLength = 60;
	DataColumn dcShipCity = new DataColumn("ShipCity", typeof(string));
	// собственно размерность по диаграмме
	dcShipCity.MaxLength = 15;
	DataColumn dcShipRegion = new DataColumn("ShipRegion", typeof(string));
	// собственно размерность по диаграмме
	dcShipRegion.MaxLength = 15;
	DataColumn dcShipPostalCode = new DataColumn("ShipPostalCode", typeof(string));
	// собственно размерность по диаграмме
	dcShipPostalCode.MaxLength = 15;
	DataColumn dcShipCountry = new DataColumn("ShipCountry", typeof(string));
	// собственно размерность по диаграмме
	dcShipCountry.MaxLength = 15;
	//-------------------- Добавляем созданные колонки в таблицы -------------------
	dtEmployees.Columns.Add(dcEmployeeID);
	dtEmployees.Columns.Add(dcLastName);
	dtEmployees.Columns.Add(dcFirstName);
	dtEmployees.Columns.Add(dcTitle);
	dtEmployees.Columns.Add(dcTitleOfCourtesy);
	dtEmployees.Columns.Add(dcBirthDate);
	dtEmployees.Columns.Add(dcHireDate);
	dtEmployees.Columns.Add(dcAddress);
	dtEmployees.Columns.Add(dcCity);
	dtEmployees.Columns.Add(dcRegion);
	dtEmployees.Columns.Add(dcPostalCode);
	dtEmployees.Columns.Add(dcCountry);
	dtEmployees.Columns.Add(dcHomePhone);
	dtEmployees.Columns.Add(dcExtension);
	dtEmployees.Columns.Add(dcPhoto);
	dtEmployees.Columns.Add(dcNotes);
	dtEmployees.Columns.Add(dcReportsTo);
	dtEmployees.Columns.Add(dcPhotoPath);

	dtOrders.Columns.Add(dcOrderID);
	dtOrders.Columns.Add(dcCustomerID);
	dtOrders.Columns.Add(dcEmployeeIDOrd);
	dtOrders.Columns.Add(dcOrderDate);
	dtOrders.Columns.Add(dcRequiredDate);
	dtOrders.Columns.Add(dcShippedDate);
	dtOrders.Columns.Add(dcShipVia);
	dtOrders.Columns.Add(dcFreight);
	dtOrders.Columns.Add(dcShipName);
	dtOrders.Columns.Add(dcShipAddress);
	dtOrders.Columns.Add(dcShipCity);
	dtOrders.Columns.Add(dcShipRegion);
	dtOrders.Columns.Add(dcShipPostalCode);
	dtOrders.Columns.Add(dcShipCountry);

Но я хочу Вас обрадовать, что это далеко не вся работа, хотя большую часть мы все-таки уже проделали.

Осталось дело за малым: Добавить ограничения иотношения и, собственно, создать сам dataset, куда добавятся наши таблички. Сделаем?!

 	...
	dtOrders.Columns.Add(dcShipCountry);
	//-------------------- добавим ключевые поля -----------------------------------
	dtEmployees.PrimaryKey = new DataColumn[]{ dcEmployeeID };
	dtOrders.PrimaryKey = new DataColumn[]{ dcOrderID };
	
	//-------------------- Добавим связи -------------------------------------------
	
	ForeignKeyConstraint fkEmployeeOrders = new ForeignKeyConstraint( "fk_EmployeeOrders",
	dcEmployeeID, dcEmployeeIDOrd);
	
	ForeignKeyConstraint fkEmployee = new ForeignKeyConstraint( "fk_Employee",
	dcEmployeeID, dcReportsTo);
				
	dtEmployees.Constraints.Add(fkEmployee);
	dtOrders.Constraints.Add(fkEmployeeOrders);
	//-------------------- Создаем собственно датасет и добавляем в него таблицы.-----
	
	DataSet ds = new DataSet("EmployeeOrders");
				
	ds.Tables.Add(dtEmployees);
	ds.Tables.Add(dtOrders);
	

Вроде как порядок. Осталось только теперь сгенерировать нашу схему. Давайте попробуем это сделать. И напишем для этого следующий код:

  ds.Tables.Add(dtOrders);
	//------------------- Генерируем схему -------------------------------------------- 
	ds.WriteXmlSchema("FirstTypedSchems.xsd");
	Console.WriteLine("Сreated !!!");

После исправления всех ошибок, а их, по правде сказать, было немало, мы получили искомый файл: "FirstTypedSchems.xsd". Ошибки вызваны были в основном из-за копирования блоков кода, для ускорения набора.

Я не буду показывать содержимое кода файла xsd. Вы, я думаю, просмотрите его и сами, откомпилировав пример, но в дизайнере студии он смотрится, мягко говоря, ужасно :)

Надо сказать откровенно, что весьма не короткий путь мы проделали на, в принципе, тривиальной задаче создания полновесного экземпляра Dataset.

Проект "FirstTyped Вы можете загрузить здесь.

Конечно если бы приходилось так создавать датасеты и схемы, то однозначно это не имело бы смысла, ввиду трудоёмкости и большой вероятности допущенных ошибок.

Давайте посмотрим на несколько другой вариант создания схемы.

Создание схемы Dataset при помощи DataAdapter и вывод ее в файл схемы (XSD).

Поистине Ado.Net позволяет решать одну и ту же задачу несколькими путями. И я предлагаю Вам в этом убедиться.

Создадим второй проект под названием «SecondTyped». Он тоже будет консольным и будет делать в принципе то же, что и предыдущий пример – генерировать схему датасета в xsd файл.

После небольших «косметических» изменений получим следующий код:

using System;
using System.Data;
using System.Data.SqlClient;

namespace SecondTyped
{
	/// 
	/// Summary description for Class1.
	/// 
	class SecondTypedClass
	{
		/// 
		/// The main entry point for the application.
		/// 
		[STAThread]
		static void Main(string[] args)
		{

		}
	}
}

Хочу отметить, что ввиду того, что проект консольный все, что вы видите, пишется исключительно руками, ну естественно, кроме того, что уже создал «мастер» на начальном этапе. Итак, в предыдущем примере мы создавали датасет, так сказать, субъективно. Мы отдались целиком во власть внимания, просто копируя структуры с той информации, которую мы видели на созданной нами перед этим диаграмме.

Здесь ситуация несколько иначе. Мы попытаемся получить всю схему из сервера базы данных, используя классы SqlConnection, SqlDataAdapter.

Итак, создаём экземпляр Connection с нашей базой данных NorthWind:

	SqlConnection conn = new SqlConnection("SERVER=localhost;DATABASE=Northwind;UID=sa;PWD=");

Я думаю, что все параметры в этой строке понятны, и не требуют дополнительных разъяснений.

Далее создаём адаптеры для наших таблиц:

 	SqlDataAdapter daEmployees = new SqlDataAdapter("Select * from Employees", conn);
	SqlDataAdapter daOrders = new SqlDataAdapter("Select * from Orders", conn);

Здесь создаётся строка запроса к БД, и собственно экземпляр Connection. Далее нам нужно создать экземпляр DataSet, в который мы собственно и будем добавлять наши таблицы.

  DataSet ds = new DataSet("EmployeesOrders");

После этого мы вызовем метод SqlDataAdapter, который даст нам возможность загрузки схемы в датасет, а именно FillSchema:


	daEmployees.FillSchema(ds, SchemaType.Source, "Employees");
	daOrders.FillSchema(ds, SchemaType.Source, "Orders");

В параметрах этого метода, мы указываем dataset (ds), куда, собственно, мы и грузим схему, тип схемы (Mapped (уже используемый) или Source (исходный)) и название таблицы, под которым эта таблица будет известна в датасете.

Осталось сохранить нашу схему в файл.

	ds.WriteXmlSchema("SecondTyped.xsd");
	

Вот полный код того, что должно было получиться:

static void Main(string[] args)
{
	try
	{
		// Создаём экземпляр подключения к базе данных NorthWind 
		SqlConnection conn = new SqlConnection("SERVER=localhost;DATABASE=Northwind;UID=sa;PWD=");
		// Создаём адаптеры для таблиц Employees и Orders
		SqlDataAdapter daEmployees = new SqlDataAdapter("Select * from Employees", conn);
		SqlDataAdapter daOrders = new SqlDataAdapter("Select * from Orders", conn);

		// Создаём экземпляр Dataset 
		DataSet ds = new DataSet("EmployeesOrders");

		// Заполняем датасет схемой таблиц из базы данных.
		daEmployees.FillSchema(ds, SchemaType.Source, "Employees");
		daOrders.FillSchema(ds, SchemaType.Source, "Orders");

		// И, собственно сохраняем схему.
		ds.WriteXmlSchema("SecondTyped.xsd");
		Console.WriteLine("Created !");

	}
	catch(Exception ex)
	{
		Console.WriteLine(ex.Message);
	}
}

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

Ладно, мы не расстраиваемся и добавляем несколько строк кода из проекта FirstTyped с небольшой модификацией:

	daEmployees.FillSchema(ds, SchemaType.Source, "Employees");
	daOrders.FillSchema(ds, SchemaType.Source, "Orders");
	
	// Добавим связи 
	ForeignKeyConstraint fkEmployeeOrders = new ForeignKeyConstraint( "fk_EmployeeOrders",
	ds.Tables["Employees"].Columns["EmployeeID"],
	ds.Tables["Orders"].Columns["EmployeeID"]);
	
	ForeignKeyConstraint fkEmployee = new ForeignKeyConstraint( "fk_Employee",
	ds.Tables["Employees"].Columns["EmployeeID"],
	ds.Tables["Employees"].Columns["ReportsTo"]);
				
	ds.Tables["Employees"].Constraints.Add(fkEmployee);
	ds.Tables["Orders"].Constraints.Add(fkEmployeeOrders);
	
	// И, собственно, сохраняем схему.
	ds.WriteXmlSchema("SecondTyped.xsd");
	Console.WriteLine("Created !");

В результате выполнениея этого проекта получится та же схема, что и полученная в FirstTyped проекте. Но, согласитесь, сил в это раз было потрачено намного меньше :).

И все же, все равно это трудоемко и не совсем приятно, но, честно говоря, уже вполне приемлемо. Давайте рассмотрим дальнейшие методы по генерации схемы. А проект SecondTyped можно загрузить здесь

Создание схемы Dataset при помощи Server Explorer.

Переходим к тому, что называется «мастером» или то, что еще называется в среде разработчиков с легким оттенком сарказма, «роботом».

Я предлагаю Вам создать третий проект под названием «ThirdTyped».

Какого типа будет этот проект не важно. Его можно сделать консольным или типа Class Library, суть этого особенно не коснется, и чуть позже вы поймете почему.

Для разнообразия я предлагаю Вам создать Class Library проект, а сам я останусь, верен традиции и создам, очередную, «консольку».

Создаём проект «ThirdTyped».

И, как всегда, немного «подшаманив», получаем следующий код:

using System;

namespace ThirdTyped
{
	/// 
	/// Summary description for Class1.
	/// 
	class ThirdTypedClass
	{
		/// 
		/// The main entry point for the application.
		/// 
		[STAThread]
		static void Main(string[] args)
		{
			//
			// TODO: Add code to start application here
			//
		}
	}
}

Теперь важная часть: нам нужно в наш уже созданный проект добавить еще один класс, который будет описывать наш датасет. VS.Net предлагает для этого специальный тип класса: «DataSet». Чтобы вставить файл с будущим датасетом перейдите в окно Solution Explorer и нажмите правую клавишу мыши, предварительно выбрав название проекта.

После того как Вы нажмете кнопку «Add New Item…», Вы увидите следующую форму:

В которой Вам, как и мне, нужно выбрать файл типа «Data Set». И, не корректируя предложенное название (Dataset1.xsd), нажать кнопку «Open».

В результате этих действий в Вашем проекте создастся файл dataset и автоматически дизайнер откроет его для редактирования.

Здесь разработчики из Microsoft любезно написали, что же Вам собственно нужно делать дальше. А предлагают они Вам воспользоваться Server Explorer–ом или же ToolBox-ом. Давайте последуем их совету и откроем Server Explorer. И выберем две нужные нам таблички:

После того, как мы это успешно осуществили, перетягиваем таблички на поверхность дизайнера для dataset. И получилось у нас вот что:

Надо сказать, симпатично. Мы видим, как вытянута вся информация из табличек. А на самом деле, дизайнер запускает тот же метод, что и мы с Вами вместе в предыдущем примере SecondTyped, и название этого метода: FillSchema. О чем свидетельствует еще и тот факт, что «Relations» нужно добавить, все так же, руками. Ну и ничего. Нужно, же нам для чего-нибудь открыть ToolBox, и увидеть все возможности работы с dataset в дизайнере? :)

Мы сразу видим то, что нам нужно: Relations. Давайте попытаемся им воспользоваться. После того, как мы выбрали пункт Relations, мы переходим в окно дизайнера и пробуем создать связать между двумя таблицами. Для этого, мы должны выбрать поле EmployeeID из таблицы Employees, и после этого потянуть «связь» не отпуская мышки, в сторону таблицы OrderID

Здесь прекрасно видно, какого типа реляцию мы создаём. Давайте пока не вдаваться в подробности и оставим те параметры, что нам предложены «по умолчанию». После установки всех реляций мы получим следующую картину.

И если, после всех наших экспериментов, посмотреть на файл DataSet1.xsd, находящемся в директории с нашим проектом, то мы увидим, что это точно такой же файл, который мы уже делали ранее, в наших предыдущих проектах.

Кроме того, мы легко можем переключиться в дизайнере из графического представления схемы, в код самого файла xsd, нажав на кнопку «XML» в нижней части дизайнера dataset в IDE:

Причем справедливости ради надо отметить и тот факт, что полученный xsd файл, при помощи дизайнера намного более компактный, чем получаемые нами ранее. Кроме того, мы видим, что дизайнер умудрился сгенерировать нам уже «типизированные» классы для нашего dataset.

Но сейчас для нас это не важно. «Типизированные» классы мы рассматривали в предыдущей статье и прекрасно знаем что это, для чего они нам нужны.

Вот такое, надо заметить, крайне удобное средство для визуальной разработки предоставляет IDE VS.Net.

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

Загрузить ThirdTyped вы можете здесь.

Создание схемы Dataset при помощи ToolBox .

Есть такое «интересно-удобное» средство для разработчика, как ToolBox :). Сейчас мы попытаемся использовать и его. Создадим, пожалуй, еще один проект под названием FourthTyped. Все та же «консолька». Добавим в него файл DataSet при помощи мастера, так же, как мы это делали в предыдущем проекте.

После всех наших манипуляций, мы должны получить поверхность дизайнера dataset:

Теперь есть смысл вернуться в начало статьи и бегло прочитать и вспомнить описание тегов xsd файла, для того, чтобы знать, с чем мы будем проводить эксперименты.

Лично я бы начал с того, что посоветовал бы Вам «поиграть» с элементом (element). Давайте попробуем. После того, как мы выбрали в ToolBox тег element и перетащили его на экран, мы увидели, что дизайнер создал нам прототип таблички, почти как в каком-нибудь «case» средстве:

Пусть эта табличка будет нашим датасетом, куда мы впишем название наших таблиц, принимающих участие в разработке, а именно Employees и Orders. Я предлагаю Вам попробовать создать небольшую иерархию, перетаскивая для этого с TollBox окошка только элементы. Причем делать это нужно специфично. После того как мы создали element с именем нашего будущего dataset, новый element с ToolBox мы перетаскиваем, как бы «вовнутрь» уже существующего элемента, создавая тем самым иерархию. Сделаем это дважды, и оба раза перетаскивая новые element «вовнутрь» таблицы myDatasSet. В результате, мы должны получить следующую таблицу:

Как Вы видите, здесь уже явно видна иерархия, причем после переименования зависимых element-ов, происходит синхронизация и в основном element, где хранится информация о «детях». Ну что же, остаётся только определить все столбцы для наших таблиц с их типами.

Ввиду своей природной лени, я обратился к предыдущему проекту, где схему сгенерировал «мастер» IDE VS.Net с надеждой на то, что мне удасться скопировать наименования колонок и их типов. И не ошибся :). За счет этого я лишил себя, и я надеюсь и Вас от длительного и неприятного набора информации о dataset.

И вот что у меня получилось в результате:

Красиво. Осталось добавить информацию о ключевых полях и связях. Отлично, давайте займемся ключами. Выбираем тег из ToolBox, который так и называется Key, и перетаскиваем его на таблицу. В результате «кидка» Key на нашу таблицу, получаем диалоговое окошко, с просьбой ввести информацию о «первичном» ключе. Заполним его:

После того, как мы ввели все изменения, нажимаем Ok и видим появившийся ключик на поле EmployeeID. Значит, мы таки сделали его ключевым. Проделаем ту же операцию и с табличкой Orders и полем OrderID. В результате всех наших «телодвижений» получаем такой «расклад»:

Ну что же, «последний штрих» как сказал бы художник, и надо сказать, был бы абсолютно прав. Кстати, мы сейчас с Вами проделали работу не сильно уступающую «мастеру кисти» :).

Добавим «relations» в наш dataset. Выбираем тег relations из ToolBox и создаем связи.

Знакомая форма? Действительно, в предыдущем проекте мы уже проделывали сию манипуляцию, а потому мы быстро проходим данный экран, и не без самодовольства замечаем, что мы уже что-то знаем :).

После того как мы это сделаем между таблицами Employees и Orders, а также связь сам на себя на табличке Employees, получим следующую картинку:

«Вуаля»! Наш датасет готов. Проверив, стоит ли автогенерация классов типизированного датасета (правая клавиша мыши на окне дизайнера датасет), мы можем закрыть проект и полюбоваться нашим xsd файлом в директории проекта или же в том же дизайнере перейдя в режим XML отображения.

После генерации DataSet классов мы увидели, что и myDataSet стал объектом для генерации. На самом деле, я сделал это умышлено. И сделано это было для того, что бы вы открыли новую для себя «веху». А именно то, что типизированные датасеты позволяют хранить информацию не только о строках, как о DataRow, а и о строках, как о таблицах DataTable!!! Вот где простор для фантазии. Это почти готовая схема репозитория для таблиц.

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

Загрузить проект FourthTyped вы можете здесь.

Теперь подробнее о том, что внутри.

Все те проекты, которые мы с Вами сделали, в принципе делают одно и то же, а именно: они предлагают Вам создание файла типизированного dataset в формате XML Schema Definition. Данный стандарт становится общепринятым в передаче схем, а также для реконструкции схем на различных платформах серверов баз данных. IDE VS.Net тоже хранит схемы datset в XML, и имеет весьма удобные методы сериализации.

Но давайте раскроем код того dataset, который мы четырежды (с небольшими отличиями) создавали в наших с Вами проектах.

Возьмем «текст» схемы (xsd файла) из последнего проекта.

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="Dataset1" targetNamespace="http://tempuri.org/Dataset1.xsd" elementFormDefault="qualified" 
  attributeFormDefault="qualified" xmlns="http://tempuri.org/Dataset1.xsd" xmlns:mstns="http://tempuri.org/Dataset1.xsd" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

В приведенном фрагменте кода, мы видим следующую информацию о нашем dataset. Здесь есть его название: "Dataset1", есть формат кодировки: "utf-8", а также ряд атрибутов, которые выставляет сам дизайнер согласно спецификации W3C. И тоже являющейся неотъемлемой информацией о нашем с Вами dataset.

<xs:element name="Dataset1" msdata:IsDataSet="true">
  <xs:complexType>
    <xs:choice maxOccurs="unbounded">
      <xs:element name="myDataSet">

Вот описание непосредственно таблицы DataSet1 которая, как вы поняли, содержит в себе еще две таблицы: Employees и Orders.

В xsd файле очень легко заметна иерархия вложенности. Заметьте, открыт element DataSet1, далее по «этажерке», открыт element Employees, в котором в свою очередь открываются elements колонки, с описанием их типа, и специфических особенностей: автоинкремента и .т.п. Давайте взглянем на отображение таблицы Employees:

 
        <xs:complexType>
          <xs:sequence>
            <xs:element name="Employes">
              <xs:complexType>
                <xs:sequence>
                   <xs:element name="EmployeeID" msdata:ReadOnly="true" msdata:AutoIncrement="true" type="xs:int" />
                   <xs:element name="LastName" type="xs:string" />
                  <xs:element name="FirstName" type="xs:string" />
                   <xs:element name="Title" type="xs:string" minOccurs="0" />
                   <xs:element name="TitleOfCourtesy" type="xs:string" minOccurs="0" />
                   <xs:element name="BirthDate" type="xs:dateTime" minOccurs="0" />
                   <xs:element name="HireDate" type="xs:dateTime" minOccurs="0" />
                   <xs:element name="Address" type="xs:string" minOccurs="0" />
                   <xs:element name="City" type="xs:string" minOccurs="0" />
                   <xs:element name="Region" type="xs:string" minOccurs="0" />
                   <xs:element name="PostalCode" type="xs:string" minOccurs="0" />
                   <xs:element name="Country" type="xs:string" minOccurs="0" />
                   <xs:element name="HomePhone" type="xs:string" minOccurs="0" />
                   <xs:element name="Extension" type="xs:string" minOccurs="0" />
                   <xs:element name="Photo" type="xs:base64Binary" minOccurs="0" />
                   <xs:element name="Notes" type="xs:string" minOccurs="0" />
                   <xs:element name="ReportsTo" type="xs:int" minOccurs="0" />
                   <xs:element name="PhotoPath" type="xs:string" minOccurs="0" />
                </xs:sequence>
              </xs:complexType>
            </xs:element>

Я думаю, что нет нужды объяснять каждый тег, он и без схемы синтаксиса, которую я приводил в начале статьи, крайне прост и интуитивно понятен. Но это ни как не значит, что Вам не нужно изучать XML / XSD – моя статья лишь претендует на то, что бы дать Вам небольшой толчок в этом направлении.

Но я немного отвлекся. А что мы видим? А видим, как закрылся тег для таблички Employees, а это значит что описание таблицы законченно. Посмотрим на дальнейший код, и как и можно было предположить, там идет описание таблицы Orders.

        <xs:element name="Orders">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="OrderID" msdata:ReadOnly="true" msdata:AutoIncrement="true" type="xs:int" />
              <xs:element name="CustomerID" type="xs:string" minOccurs="0" />
              <xs:element name="EmployeeID" type="xs:int" minOccurs="0" />
              <xs:element name="OrderDate" type="xs:dateTime" minOccurs="0" />
              <xs:element name="RequiredDate" type="xs:dateTime" minOccurs="0" />
              <xs:element name="ShippedDate" type="xs:dateTime" minOccurs="0" />
              <xs:element name="ShipVia" type="xs:int" minOccurs="0" />
              <xs:element name="Freight" type="xs:decimal" minOccurs="0" />
              <xs:element name="ShipName" type="xs:string" minOccurs="0" />
              <xs:element name="ShipAddress" type="xs:string" minOccurs="0" />
              <xs:element name="ShipCity" type="xs:string" minOccurs="0" />
              <xs:element name="ShipRegion" type="xs:string" minOccurs="0" />
              <xs:element name="ShipPostalCode" type="xs:string" minOccurs="0" />
              <xs:element name="ShipCountry" type="xs:string" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>

Все, описание таблички Orders, как и ранее Employees, закончено. Все так же просто и наглядно, вот название элемента, вот его тип, вот обозначение того, что данная колонка является значением только для чтения. Что у нас осталось? Неописанные отношения и ограничения, а также не закрытая «базовая» таблица DataSet1? И Вы абсолютно правы, вот оно – заключение:

            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
    <xs:key name="pkEmployee" msdata:PrimaryKey="true">
      <xs:selector xpath=".//mstns:Employes" />
      <xs:field xpath="mstns:EmployeeID" />
    </xs:key>
    <xs:key name="pkOrders" msdata:PrimaryKey="true">
      <xs:selector xpath=".//mstns:Orders" />
      <xs:field xpath="mstns:OrderID" />
    </xs:key>
    <xs:keyref name="EmployesOrders" refer="pkEmployee">
      <xs:selector xpath=".//mstns:Orders" />
      <xs:field xpath="mstns:EmployeeID" />
    </xs:keyref>
    <xs:keyref name="EmployesEmployes" refer="pkEmployee">
      <xs:selector xpath=".//mstns:Employes" />
      <xs:field xpath="mstns:ReportsTo" />
    </xs:keyref>
  </xs:element>
</xs:schema>

И венчает, точнее, завершает, описание нашего «ненаглядного» датасета тег закрытия схемы. Все.

Если честно, то мне даже немного стыдно от такого описания «внутренностей» схемы, но позвольте, я еще не встречал более, интуитивно понятной, и простой реализации модели представления схемы данных!!!

И я надеюсь, что Вы со мной согласитесь.

Кроме того, эта схема имеет еще массу весьма важных причин для того, чтобы сериализовать наши данные, или точнее сервера используя XML/XSD. И, пожалуй, наиважнейшее это то, что все возможные NAT и Proxy сервера, которые блокируют прохождение бинарных данных, позволяют работать с XML/XSD данными.

Но я снова несколько отвлекся. Это тема уже для другой статьи.

Итак, что же у нас осталось нерассмотренным? Точно - командная строка.

XSD.Exe или «командная строка».

Любовь всех тех, кто не любит разного рода IDE, и предпочитает работать в NotePad. К их «глубокому разочарованию», Microsoft и их не оставила без внимания.

Расположенная по умолчанию утилита xsd.exe находится по пути %SystemRoot%\Program Files\Microsoft.Net\FrameworkSDK\Bin либо %SystemRoot%\Program Files\Microsoft Visual Studio .Net\FrameworkSDK\Bin. Но есть маленькое «но». Утилита xsd.exe позволит Вам создать классы типизированного датасета на основании созданного Вами xsd файла. Естественно Вы не будете планировать схему базы данных руками. Я думаю, что Ваша квалификация уж точно позволит Вам использовать какое либо case средство, а они практически все позволяют экспортировать схему в xsd файл. Который вы и xsd.exe превратите в классы типизированного датасета.

Ну что же, я думаю пришло время на создание еще одного проекта? Давайте «приложим» руку к «мастеру» на предмет очередной «консольки». И назовется она, дабы не нарушать традиции, естественно «FifthTyped».

Замечательно. Как всегда немного «косметики». И, внимание, важный момент! Давайте возьмем Dataset1.xsd файл из предыдущего проекта, т.е. из «FourthTyped», скопируем его в директорию «FifthTyped»-го проекта. И после этого добавим его в проект «FifthTyped» следующим образом:

Нажмем на кнопочку Show All Files в Solution Explorer:

И увидим наш файл Dataset1.xsd. Выберем его и нажмем правую клавишу мышки:

Далее нам остаётся только выполнить пункт контекстного меню «Include In Project».

Далее мы делаем то, что уже делали раньше: двойным «кликом» по левой клавише мышки активируем дизайнер dataset:

И что же мы видим? Полновесную схему того, что мы уже сделали раньше.

Но, внимание! Теперь мы не хотим, чтобы классы генерировались автоматически с помощью IDE. Будем считать, что у нас, ее нет. Для того, чтобы убедиться что у нас не «выставленна» автогенерация, нажимаем правую клавишу мышки на полотне дизайнера dataset и отключаем опцию автогенерации классов типизированного dataset если она была включена:

Теперь мы можем выйти из IDE вообще, и потрудиться в командной строке.

После установки путей поиска на описанные выше директории, вам уже ничего не будет мешать. Давайте запустим xsd.exe в командной строке и посмотрим на тот список опций, что он нам выдаст:

Все предельно ясно. У нас есть <schema>.xsd которую нужно сгенерировать в классы типизированного датасета, написанного на языке С#. Подставляем соответствующие ключи и получаем строку вида:

xsd /d /l:c# Dataset1.xsd
  

Пробуем запустить xsd с заданными нами параметрами:

Все прошло на ура! Файл размером около 71Кб. Проделаем операции по добавлению в проект (такие же, как и в случае с добавлением файла датасета).

И скомпилируем его, для того, чтобы убедится что «все так просто»!

Все прекрасно и замечательно. Файл добавлен в проект, компилируется без единой ошибки. «Пользуйтесь», говорит Вам Microsoft :).

Мало того, теперь Ваш класс типизированного dataset еще и доступен в дизайн редиме, о чем свидетельствуют атрибуты:

[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.ComponentModel.ToolboxItem(true)]

Вообще красота. После компиляции в dll и добавления через References в другом проекте Вы имеете возможность работать с созданным Вами же классом dataset. Это открывает колоссальный простор для мобильности блоков клиентской стороны доступа к серверу БД. Но это, как говорят, «уже совсем другая история»

Загрузить пятый и последний проект «FifthTyped» - вы сможете здесь.

Послесловие.

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

Данная статья закрывает цикл статей «Работа с данными вчера, сегодня, завтра». И только. Это никак не означает, что она закрывает, что-либо еще! Статьи будут появляться все с той же периодичностью, как появлялись до сих пор. Я очень надеюсь, что Вы простите мне те огрехи, которые неминуемо встречались в данных статьях, все-таки написано было немало. Кроме того, я с радостью отвечу на интересующие Вас вопросы на нашем форуме. До новых статей!

С уважением,

Serg Vorontsov aka V©R©N




Вернуться к статьям в категории ADO.NET
Вернуться к списку категорий
Перейти к форуму ADO.NET
 
Наш Киев

Apartments for Rent

Rambler's Top100
Рейтинг@Mail.ru
Идея: Dimon aka Manowar Программирование: Dimon aka Manowar Дизайн: Dan Lebedev
Хостинг от компании Parking.ru
Карта сайта