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

Объект DataView. Вывод связанных таблиц

Разбить на страницы
Показывать лекцию целиком
Внимание! Для работы с лекциями 9, 10 необходимы учебные файлы, которые Вы можете загрузить здесь.

Объект DataView. Вывод связанных таблиц

Объект DataView. Фильтрация и сортировка данных

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

Скопируйте папку приложения VisualDataSQL из второй лекции и переименуйте ее в VisualDataView. Открываем проект и перетаскиваем на форму еще один элемент DataGrid, свойству Dock которого устанавливаем значение Bottom. Выделив добавленный элемент DataGrid, перетащим на форму элемент управления Splitter (разделитель), свойству Dock которого также установим значение Bottom. В результате на форме располагаются две таблицы, в режиме запуска размеры покрытия их можно будет изменять, передвигая разделитель. Переключаемся на вкладку Data панели инструментов Toolbox и пертаскиваем на форму элемент DataView (рис. 9.1).

(рис 9.1) Элемент управления DataView

Добавленный объект появляется на панели компонент. Прежде всего следует определить, что будет представлять источник данных для него. Переходим в окно Properties, в свойстве Table выбираем таблицу Customers объекта DataSet (рис. 9.2).

(рис 9.2) Определение таблицы объекта DataView

Теперь определим фильтр, который будет определять содержимое DataView. Выбираем свойство RowStateFilter, нажимаем на кнопку "Original Rows" (рис. 9.3, A), затем снимаем все галочки, кроме "Deleted" (рис. 9.3, Б).

(рис 9.3) Окно свойств объекта DataView

Кнопка "Original Rows" переключает на свойства первоначальных (оригинальных), исходных записей, загружаемых в DataSet. Выбрав пункт "Deleted", мы тем самым отобрали фильтр для удаленных записей.

В окне Properties второго элемента DataGrid в свойстве DataSource устанавливаем объект DataView в качестве источника данных (рис. 9.4).

(рис 9.4) Определение объекта DataView в качестве источника данных для объекта DataGrid

Запускаем приложение. Выделяем записи поля CustomerID со значениями "ANTON" и "BLAUS" и удаляем их - они немедленно появляются во втором элементе DataGrid (рис. 9.5).

(рис 9.5) Удаленные записи оказываются во второй таблице

Изменим фильтр - в поле свойства RowStateFilter нажимаем на кнопку "Current Rows" (Текущие записи) и оставляем галочки на пунктах "New" и "Current Modified" (рис. 9.6):

(рис 9.6) Установка нового фильтра

Запускаем приложение. Теперь во вторую таблицу будут помещаться текущие записи, в которые были внесены изменения - в первой записи в поле City было добавлено "123", а также новые записи - прокручиваем список до конца и вводим данные (рис. 9.7).

(рис 9.7) Применение фильтра по новым и измененным записям

Каждый объект DataSet поддерживает две версии - исходную, полученную при загрузке из базы данных, и текущую, в которую были внесены изменения. Данные, выведенные на форму в элемент управления DataGrid, можно изменять, при этом фиксируется статус записей объекта DataRow с помощью свойства RowState. Свойство RowStateFilter осуществляет фильтрацию по этому свойству, причем кнопке "Original" соответствуют исходные данные, а кнопке "Current" - текущие. Значения этого свойства приведены в таблице 9.1.

Значения свойства RowStateFilter объекта DataView
Свойство Описание
Unchanged Записи без изменений
New Новые записи
Deleted Удаленные записи
Current Modified Измененные записи с их текущими значениями
Original Modified Измененные записи с их первоначальными значениями

Как мы уже видели, для одного объекта DataView в пределах одной версии объекта DataSet (исходной или текущей) возможно объединение фильтров - например, отображение новых и удаленных записей.

Свойство RowFilter предназначено для вывода записей, содержащих определенное значение заданного поля. Установим свойству RowStateFilter значение по умолчанию - CurrentRows, - а затем в свойстве RowFilter введем фильтрацию (рис. 9.8):

City = 'London'
(рис 9.8) Свойство RowFilter объекта DataView

Запускаем приложение. Во второй таблице выводятся все записи с названием города "London" (рис. 9.9, А). Установив в свойстве RowFilter другое значение - ContactName = 'Thomas Hardy', - получаем одну запись, содержащую это значение (рис. 9.9, Б):

(рис 9.9) Вывод записей со значением "London" поля City (А) и записи со значением "Thomas Hardy поля ContactName (Б)

Свойство Sort предназначено для вывода записей в порядке возрастания (ascending, ASC) или убывания (descending, DESC) по значениям заданного поля. В принципе, элемент DataGrid сам по себе поддерживает сортировку - достаточно просто щелкнуть по заголовку поля. Однако это требует действий от пользователя, тогда как объект DataView может предоставлять данные уже в готовом виде.

Удаляем значение свойства RowFilter, в поле свойства Sort вводим "City ASC" (рис. 9.10):

(рис 9.10) Сортировка по полю City

Запускаем приложение. Во второй таблице отображаются записи со значениями поля City в порядке возрастания (рис. 9.11, А). Введем другое значение поля Sort - "ContactName DESC" - в результате получаем сортировку записей в порядке убывания по полю CompanyName (рис. 9.11, Б):

(рис 9.11) Сортировка записей по полю City (А), сортировка записей по полю ContactName (Б)

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

Приступим теперь к рассмотрению этих же свойств объекта DataView, создаваемых программным образом. Скопируйте папку приложения ProgrammDataSQL и переименуйте ее в ProgrammDataView. Добавляем на форму второй DataGrid и Splitter точно так же, как мы это сделали в предыдущем приложении. Переходим в конструктор формы и изменяем код следующим образом:

public Form1()
{
	InitializeComponent();
	SqlDataAdapter dataAdapter = new SqlDataAdapter(CommandText, ConnectionString);
	DataSet ds = new DataSet();
	//Создаем экземпляр dtCustomer объекта DataTable
	DataTable dtCustomer = ds.Tables.Add("Customer");
	dataAdapter.Fill(ds, "Customer");
	dataGrid1.DataSource = ds.Tables["Customer"].DefaultView;
	//Создаем экземпляр myDataView объекта DataView,
	//передаем ему объект dtCustomer
	DataView myDataView = new DataView(dtCustomer);
	//Устанавливаем фильтр для удаленных записей
	myDataView.RowStateFilter = DataViewRowState.Deleted;
	//Для второго элемента DataGrid указываем myDataView
	//в качестве источника данных.
	dataGrid2.DataSource = myDataView;
}

Объект DataView представляет данные объекта DataTable - именно поэтому нам пришлось создать экземпляр dtCustomer. Запускаем приложение и проверяем его функциональность - результат должен быть в точности такой же, как и ранее.

Для вывода новых строк (DataViewRowState.Added) и (символ "|" ) измененных (DataViewRowState.ModifiedCurrent) фильтр будет выглядеть так:

myDataView.RowStateFilter =
 ((DataViewRowState)((DataViewRowState.Added | DataViewRowState.ModifiedCurrent)));

Закомментируем в коде свойство RowStateFilter. Для вывода фильтров по свойству RowFilter используем следующие фрагменты кода:

//Выводим все записи со значением 'London' поля City:
myDataView.RowFilter = "City = 'London'";

Или

//Выводим все записи со значением 'Thomas Hardy' поля ContactName:
myDataView.RowFilter = "ContactName = 'Thomas Hardy'";

Закомментируем после проверки в коде эти фрагменты и добавим сортировку записей:

// Сортируем записи по полю "City" в порядке возрастания
myDataView.Sort = "City ASC";

Или

//Сортируем записи по полю ContactName в порядке убывания
myDataView.Sort = "ContactName DESC";

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

Вывод связанных таблиц. Вывод двух таблиц в один элемент DataGrid

База данных Microsoft Access BDTur_firm.mdb содержит таблицу "Туристы", которая связана с другими таблицами. Было бы удобно выводить эту таблицу в элемент DataGrid вместе с другими таблицами, а также выводить связанные записи этой таблицы. Создайте новое Windows-приложение и назовите его DataGrid2Table. Перетаскиваем на форму элемент управления DataGrid, свойству Dock устанавливаем значение Fill. Переходим в код формы и подключаем пространство имен:

using System.Data.OleDb;

В конструкторе формы создаем соединение, объект OleDbCommand, определяем для него соединение и строку CommandText:

OleDbConnection conn = new OleDbConnection(connectionString);
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = conn;
myCommand.CommandText = commandText;

Подключаемся к файлу базы данных BDTur_firm.mdb, указываем соответствующие параметры строк connectionString и commandText:

string commandText = "SELECT [Код туриста], Фамилия, Имя, Отчество FROM Туристы";
string connectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0"
 ";Data Source=""D:\Uchebnik\Code\Glava1\BDTur_firm.mdb"";
 ID=Admin;Jet OLEDB:Encrypt Database=False";

Создаем объект DataAdapter и в свойстве SelectCommand устанавливаем значение myCommand, открываем соединение:

OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
dataAdapter.SelectCommand = myCommand;
conn.Open();

Создаем объект DataSet:

DataSet ds = new DataSet();

В объекте DataSet здесь будут храниться две таблицы - главная и связанная с ней дочерняя. Поэтому воспользуемся свойством TableMappings объекта DataAdapter для занесения в него первой таблицы "Туристы":

DataSet ds = new DataSet();
dataAdapter.TableMappings.Add("Table", "Туристы");
dataAdapter.Fill(ds);

Свойству DataSource объекта dataGrid1 указываем таблицу "Туристы" объекта ds. Обратите внимание на синтаксис - свойство Tables подразумевает наличие нескольких таблиц в объекте DataSet:

dataGrid1.DataSource = ds.Tables["Туристы"].DefaultView;

Закрываем соединение:

conn.Close();

Запускаем приложение. Пока на форме появляется только одна таблица. Закрываем приложение и переходим в код формы. Теперь нам следует добавить объекты OleDbDataAdapter и OleDbCommand для таблицы "Информация о туристах":

OleDbCommand myCommand2 = new OleDbCommand();
myCommand2.Connection = conn;
myCommand2.CommandText = commandText2;
OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();

Обратите внимание, что dataAdapter2 использует то же самое подключение conn, что и dataAdapter.

Строку commandText2 определим следующим образом:

string commandText2 = "SELECT [Код туриста], [Серия паспорта],
 Город, Страна, Телефон, Индекс FROM [Информация о туристах]";

Теперь свяжем второй объект OleDbDataAdapter с только что созданной второй командой и отобразим "Информацию о туристах" на его таблицу. Затем можно заполнить объект DataSet данными из второй таблицы:

dataAdapter2.SelectCommand = myCommand2;
dataAdapter2.TableMappings.Add("Table", "Информация о туристах");
dataAdapter2.Fill(ds);

В итоге у нас получился объект DataSet с двумя таблицами. Теперь можно выводить одну из этих таблиц на форму, или две сразу. Но связь между таблицами еще не создана. Для конфигурирования отношения по полю "Код туриста" создаем два объекта DataColumn:

DataColumn dcTouristsID =
 ds.Tables["Туристы"].Columns["Код туриста"];
DataColumn dcInfoTouristsID =
 ds.Tables["Информация о туристах"].Columns["Код туриста"];

Создаем объект DataRelation, в его конструкторе передаем название отношения между таблицами и два объекта DataColumn:

DataRelation dataRelation =
 new DataRelation("Дополнительная информация", dcTouristsID, dcInfoTouristsID);

Добавляем созданный объект отношения к объекту DataSet:

ds.Relations.Add(dataRelation);

Создаем объект DataViewManager, отвечающий за отображение DataSet в объекте DataGrid:

DataViewManager dsview = ds.DefaultViewManager;

Присваиваем свойству DataSource объекта DataGrid созданный объект DataViewManager:

dataGrid1.DataSource = dsview;

Последнее, что нам осталось сделать, - сообщить объекту DataGrid, какую таблицу считать главной (родительской) и, соответственно, отображать на форме:

dataGrid1.DataMember = "Туристы";

Закрываем соединение:

conn.Close();

Запускаем приложение. Теперь можно просматривать связанные записи (рис. 9.12):

(рис 9.12) Для перехода на дочернюю запись нажимаем на ссылку "Дополнительная информация". Для возвращения на родительскую запись нажимаем на кнопку со стрелкой

Обратите внимание, что здесь мы не стали накладывать ограничения - это сделано для упрощения кода.

Конструктор формы будет иметь следующий вид:

public Form1()
{
	InitializeComponent();
	OleDbConnection conn = new OleDbConnection(connectionString);
	OleDbCommand myCommand = new OleDbCommand();
	myCommand.Connection = conn;
	myCommand.CommandText = commandText;
	OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
	dataAdapter.SelectCommand = myCommand;
	conn.Open();
			
	DataSet ds = new DataSet();
	dataAdapter.TableMappings.Add("Table", "Туристы");
	dataAdapter.Fill(ds);
		
	OleDbCommand myCommand2 = new OleDbCommand();
	myCommand2.Connection = conn;
	myCommand2.CommandText = commandText2;
	OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();
	dataAdapter2.SelectCommand = myCommand2;
	dataAdapter2.TableMappings.Add("Table", "Информация о туристах");
	dataAdapter2.Fill(ds);
		
	DataColumn dcTouristsID = ds.Tables["Туристы"].Columns["Код туриста"];
	DataColumn dcInfoTouristsID =
	 ds.Tables["Информация о туристах"].Columns["Код туриста"];
	DataRelation dataRelation =
	 new DataRelation("Дополнительная информация", dcTouristsID, dcInfoTouristsID);
	ds.Relations.Add(dataRelation);
	DataViewManager dsview = ds.DefaultViewManager;
	dataGrid1.DataSource = dsview;
	dataGrid1.DataMember = "Туристы";
	conn.Close();
}

Закомментируем фрагмент кода, отвечающий за создание связи:

//DataColumn dcTouristsID = ds.Tables["Туристы"].Columns["Код туриста"];
//DataColumn dcInfoTouristsID =
// ds.Tables["Информация о туристах"].Columns["Код туриста"];
//DataRelation dataRelation =
// new DataRelation("Дополнительная информация", dcTouristsID, dcInfoTouristsID);
//ds.Relations.Add(dataRelation);
DataViewManager dsview = ds.DefaultViewManager;
dataGrid1.DataSource = dsview;
//dataGrid1.DataMember = "Туристы";
conn.Close();

Теперь в приложении будут отдельные ссылки на две таблицы - "Туристы" (рис. 9.13, А) и "Информация о туристах" (рис. 9.13, Б):

(рис 9.13) Переход на две таблицы - "Туристы" (А) и "Информация о туристах" (Б)

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

Вывод связанных таблиц в два элемента DataGrid1

Наиболее часто встречаемая задача при разработке приложений, связанных с базами данных: одновременный вывод двух таблиц на форму, причем при перемещении по записям главной таблицы в дочерней автоматически отображаются связанные записи. Мы сделаем два приложения - одно будет создано с использованием визуальных инструментов студии, другое - полностью программно. Создайте новый Windows-проект и назовите его Visual2DataGrid2Table. Из окна Toolbox перетаскиваем на форму элемент DataGrid, свойству Dock этого элемента устанавливаем значение Bottom. Добавляем элемент Splitter, его свойству Dock также устанавливаем значение Bottom. Наконец, перетаскиваем второй DataGrid, свойству Dock устанавливаем значение Fill. Дополнительно определяем следующие свойства элементов:

dataGrid1, свойство Значение
Name dgInfoTourists
CaptionText Информация о туристах
dataGrid2, свойство Значение
Name dgTourists
CaptionText Туристы

Переходим на вкладку Data окна Toolbox, перетаскиваем на форму элемент (...). В редакторе Tables Collection Editor нажимаем кнопку "Add" для добавления таблицы "Туристы" (TableName - "Туристы", Name - "dtTourists"). В поле свойства (...) и создаем следующие столбцы:

Поле Column1, свойство Значение
ColumnName Код туриста
DataType System.Int32
Unique True
Name dcTouristID
Поле Column2, свойство Значение
ColumnName Фамилия
Name dcLastName
Поле Column3, свойство Значение
ColumnName Имя
Name dcFirstName
Поле Column4, свойство Значение
ColumnName Отчество
Name dcMiddleName

Аналогично, в редакторе Tables Collection Editor создаем таблицу "Информация о туристах", а затем в редакторе Columns Collection Editor создаем ее поля:

TableName Информация о туристах
Name dtInfoTourists
Поле Column1, свойство Значение
ColumnName Код туриста
DataType System.Int32
Unique True
Name dcInfoTouristsID
Поле Column2, свойство Значение
ColumnName Серия паспорта
Name dcPassport
Поле Column3, свойство Значение
ColumnName Город
Name dcCity
Поле Column4, свойство Значение
ColumnName Страна
Name dcCountry
Поле Column5, свойство Значение
ColumnName Телефон
Name dcPhone
Поле Column6, свойство Значение
ColumnName Индекс
Name dcIndex

Завершив настройку таблиц и колонок, переходим к определению связи между таблицами. Выделяем объект (...). В появившемся редакторе Relation Collection Editor нажимаем кнопку "Add" и создаем отношение "Дополнительная_информация" между таблицами по ключевому полю "Код туриста". Завершив работу с редактором, переходим в код формы. Подключаем пространство имен для работы с базой данных Microsoft Access:

using System.Data.OleDb;

Добавляем строки для подключения к базе и извлечения данных:

string connectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0"";
 Data Source=""D:\Uchebnik\Code\Glava1\BDTur_firm.mdb"";
 User ID=Admin;Jet OLEDB:Encrypt Database=False";
string commandText = "SELECT [Код туриста], Фамилия, Имя, Отчество FROM Туристы";
string commandText2 = "SELECT [Код туриста], [Серия паспорта],
 Город, Страна, Телефон, Индекс FROM [Информация о туристах]";

В конструкторе формы создаем объекты для заполнения данными DataSet:

public Form1()
		{
			
			InitializeComponent();

			OleDbConnection conn = new OleDbConnection(connectionString);
			OleDbCommand myCommand = new OleDbCommand();
			myCommand.Connection = conn;
			myCommand.CommandText = commandText;
			OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
			dataAdapter.SelectCommand = myCommand;

			OleDbCommand myCommand2 = new OleDbCommand();
			myCommand2.Connection = conn;
			myCommand2.CommandText = commandText2;
			OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();
			dataAdapter2.SelectCommand = myCommand2;
		
			conn.Open();
			dataAdapter.Fill(dsTourists.Tables["Туристы"]);
			dataAdapter2.Fill(dsTourists.Tables["Информация о туристах"]);
			conn.Close();
	
			dgTourists.DataSource = dsTourists;
			dgTourists.DataMember = "Туристы";

			dgInfoTourists.DataSource = dsTourists;
			dgInfoTourists.DataMember = "Туристы.Дополнительная_информация";
		}

Запускаем приложение (рис. 9.14):

(рис 9.14) Готовое приложение Visual2DataGrid2Table. При перемещении по записям таблицы "Туристы" автоматически выводятся связанные записи в таблице "Информация о туристах"

Ключевым моментом здесь является определение связи "Дополнительная_информация" в содержимом элемента dgInfoTourists:

dgInfoTourists.DataMember = "Туристы.Дополнительная_информация";

Эта же связь доступна и в окне родительской таблицы - при нажатии на гиперссылку возвращается связанная запись.

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

Рассмотрим теперь программную реализацию этого же приложения. Создайте новый Windows-проект и назовите его "Programm2DataGrid2Table". Скопируем из проекта Visual2DataGrid2Table два элемента DataGrid, Splitter и вставим в новую форму. Пространство имен, строки connectionString и commandText будут в точности такие же, как и в предыдущем проекте, - просто скопируйте их. Конструктор формы будет иметь следующий вид:

public Form1()
{
	InitializeComponent();
	// Создаем объект dtTourists для таблицы "Туристы"
	DataTable dtTourists = new DataTable("Туристы");
	DataColumn dcTouristID = new DataColumn("Код туриста", typeof(int));
	dcTouristID.Unique = true;
	DataColumn dcLastName = new DataColumn("Фамилия",typeof(string));
	DataColumn dcFirstName = new DataColumn("Имя", typeof(string));
	DataColumn dcMiddleName = new DataColumn("Отчество", typeof(string));
	dtTourists.Columns.AddRange(new DataColumn[] {
		dcTouristID, 
		dcLastName, 
		dcFirstName, 
		dcMiddleName });
	// Создаем объект dtInfoTourists для таблицы "Информация о туристах"
	DataTable dtInfoTourists = new DataTable("Информация о туристах");
	DataColumn dcInfoTouristsID = new DataColumn("Код туриста", typeof(int));
	dcInfoTouristsID.Unique = true;
	DataColumn dcPassport = new DataColumn("Серия паспорта", typeof(string));
	DataColumn dcCity = new DataColumn("Город", typeof(string));
	DataColumn dcCountry = new DataColumn("Страна", typeof(string));
	DataColumn dcPhone	= new DataColumn("Телефон", typeof(decimal));
	DataColumn dcIndex	= new DataColumn("Индекс", typeof(decimal));
					
	dtInfoTourists.Columns.AddRange(new DataColumn[] {
		dcInfoTouristsID,
		dcPassport,
		dcCity,
		dcCountry,
		dcPhone, 
		dcIndex });

	DataSet dsTourists = new DataSet();
	// Добавляем таблицы в DataSet
	dsTourists.Tables.AddRange(new DataTable[] {
		dtTourists, 
		dtInfoTourists});		

	// Создаем отношение между таблицами
	dsTourists.Relations.Add(new DataRelation("Дополнительная_информация", 
		dcTouristID,
		dcInfoTouristsID));
			
	//Подключаемся к базе и выводим данные
	OleDbConnection conn = new OleDbConnection(connectionString);
	OleDbCommand myCommand = new OleDbCommand();
	myCommand.Connection = conn;
	myCommand.CommandText = commandText;
	OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
	dataAdapter.SelectCommand = myCommand;

	OleDbCommand myCommand2 = new OleDbCommand();
	myCommand2.Connection = conn;
	myCommand2.CommandText = commandText2;
	OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();
	dataAdapter2.SelectCommand = myCommand2;
	
	dataAdapter.Fill(dsTourists.Tables["Туристы"]);
	dataAdapter2.Fill(dsTourists.Tables["Информация о туристах"]);
	dgTourists.DataSource = dsTourists;
	dgTourists.DataMember = "Туристы";

	dgInfoTourists.DataSource = dsTourists;
	dgInfoTourists.DataMember = "Туристы.Дополнительная_информация";
}

Запускаем приложение - его функциональность должна быть в точности такая же, как и в проекте Visual2DataGrid2Table.

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

Свойство PrimaryKey

Мы научились конструировать структуру таблицы в объекте DataSet, а также определять отношения между таблицами. Во всех случаях для выделения первичного ключа в таблице использовалось свойство Unique. Например, в самом последнем примере - проекте Programm2DataGrid2Table - первичный ключ "Код туриста" определялся так:

...
DataColumn dcTouristID = new DataColumn("Код туриста", typeof(int));
dcTouristID.Unique = true;
...

DataColumn dcInfoTouristsID = new DataColumn("Код туриста", typeof(int));
dcInfoTouristsID.Unique = true;
...

Для вывода таблиц идентификации записей этого определения вполне хватало. Однако свойство Unique всего лишь указывает на уникальность заданного поля, т.е. на отсутствие повторяющихся записей. В самом деле, в таблице может быть несколько полей, которые должны быть уникальными и одно из них (или их комбинация) будут образовывать первичный ключ. Для указания именно первичного ключа используется свойство PrimaryKey объекта DataTable:

DataTable dtTourists = new DataTable("Туристы");
DataColumn dcTouristID = new DataColumn("Код туриста", typeof(int));
dtTourists.PrimaryKey = new DataColumn [] {dtTourists.Columns["Код туриста"]};

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

dtTourists.Columns.Add("Код туриста", typeof(int));
dtTourists.PrimaryKey = new DataColumn [] {dtTourists.Columns["Код туриста"]};

Можно применять комбинацию полей для задания первичного ключа:

DataTable dtTourists = new DataTable("Туристы");
DataColumn dcTouristID =
 new DataColumn("Код туриста", typeof(int));
DataColumn dcLastName =
 new DataColumn("Фамилия",typeof(string));
dtTourists.PrimaryKey =
 new DataColumn [] {dtTourists.Columns["Код туриста"],
 dtTourists.Columns["Фамилия"]};

Здесь первичным ключом будут значения поля "Код туриста" в сочетании со значением поля "Фамилия".

При конструировании объекта DataTable в редакторе Tables Collection Editor после создания объектов Columns в свойстве PrimaryKey можно выделять одно или несколько полей - они отмечаются числами и становятся первичным ключом (рис. 9.15):

(рис 9.15) Определение первичного ключа в редакторе Tables Collection Editor

После определения первичного ключа объекта DataTable для свойства AllowDBNull (разрешение значений null) объектов DataColumn, формирующих ключ, будет установлено значение false.

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

Поиск данных

Технология ADO .NET предоставляет значительные возможности для поиска данных - такие объекты, как DataSet, DataTable, содержат специализированные методы для быстрого решения этой задачи. Между тем свойство RowFilter объекта DataView может применяться для создания простого и эффективного поиска. Запускаем Visual Studio .NET и создаем новый проект. В окне New Project выбираем тип Windows Control Library, называем его "FindControl" (рис. 9.16):

(рис 9.16) Выбор шаблона Windows Control Library

Появляется форма пользовательского (композитного) элемента управления. Устанавливаем следующие свойства:

UserControl1, свойство Значение
Name FindCheckBox
Size 230; 70

В окне Solution Explorer изменяем название "UserControl1.cs" на "FindCheckBox.cs". Перетаскиваем на форму из окна Toolbox текстовое поле, надпись и элемент CheckBox. Настраиваем их свойства:

CheckBox1, свойство Значение
Name chbForSearching
Location 8; 32
Text
textBox1, свойство Значение
Name txtColumnValue
Location 32; 32
Size 184; 20
Text
label1, свойство Значение
Name lblColumnName
Location 40; 8
Text ColumnName

Переходим к коду. В конструкторе формы отключаем доступность текстового поля и привязываем обработчик события CheckedChanged для элемента CheckBox:

public FindCheckBox()
{
	InitializeComponent();
	txtColumnValue.Enabled = false;
	chbForSearching.CheckedChanged +=
	 new EventHandler(chbForSearching_CheckedChanged);
}

В классе формы создаем обработчик события CheckedChanged для CheckBox и определяем действия для значений текстового поля, надписи и элемента CheckBox:

private void chbForSearching_CheckedChanged(object sender, EventArgs e)
{
	txtColumnValue.Enabled = chbForSearching.Checked;
}
[Category("Appearance"),
 Description("Название поля, по которому будет осуществляться поиск")]
public string ColumnName
{
	get {return lblColumnName.Text;}
	set {lblColumnName.Text = value;}
}
[Category("Appearance"), Description("Ключевое слово для поиска")]
public string ColumnValue
{
	get {return txtColumnValue.Text;}
	set {txtColumnValue.Text = value;}
}
		
[Category("Appearance"), Description("Включение поиска по заданному полю")]
public bool SearchEnabled
{
	get {return chbForSearching.Checked;}
	set {chbForSearching.Checked = value;}
}

Свойства ColumnName, ColumnValue и SearchEnabled будут свойствами композитного элемента управления, которые будут отображаться в его окне Properties. В квадратных скобках указан атрибут для помещения свойства в заданную группу и описание, выводимое на информационную панель. Компилируем приложение (Ctrl+Shift+B) и закрываем его. Создаем новое Windows-приложение, называем его "FindCustomers". Перетаскиваем на форму следующие элементы управления: Panel (свойству Dock устанавливаем значение Left ), Splitter и DataGrid (свойству Dock устанавливаем значение Fill ). В окне Toolbox щелкаем правой кнопкой мыши на вкладке Windows Forms и в появившемся контекстном меню выбираем пункт "Add \ Remove Items_". В появившемся окне Customize Toolbox на вкладке .NET Framework Components нажимаем кнопку "Browse" (рис. 9.17, А) и переходим в директор ию, где расположена сборка композитного элемента FindControl (у меня это D:\Uchebnik\Code\Glava4\FindControl\bin\Debug, сборка FindControl.dll) (рис. 9.17, Б). После выбора сборки она появляется в окне Customize Toolbox (рис. 9.17, В).

(рис 9.17) Добавление композитного элемента. А - окно "Customize Toolbox", Б - Выбор сборки FindControl.dll, В - добавленная сборка на вкладке ".NET Framework Components"

Нажимаем кнопку "OK" и перетаскиваем на панель формы из окна Toolbox четыре копии элемента FindCheckBox. Добавляем также кнопку поиска (рис. 9.18). Устанавливаем следующие свойства элементов:

findCheckBox1, свойство Значение
ColumnName CustomerID
Name fcbCustomerID
findCheckBox2, свойство Значение
ColumnName CompanyName
Name fcbCompanyName
findCheckBox3, свойство Значение
ColumnName ContactName
Name FcbContactName
findCheckBox4, свойство Значение
ColumnName Phone
Name fcbPhone
button1, свойство Значение
Name btnSearch
Text Поиск
(рис 9.18) Расположение элементов на форме. Окно Properties композитного элемента

Обратите внимание (рис. 9.18), что в окне Properties, при групповом расположении, свойство ColumnName находится в группе Appearance. На информационную панель выводится описание этого свойства на русском языке. Именно эти параметры мы и указывали при создании композитного элемента.

Для того чтобы упростить код, создадим подключение к базе данных при помощи визуальных средств студии. В окне Toolbox переходим на вкладку Data, перетаскиваем на форму SqlDataAdapter и настраиваем извлечение таблицы Customers базы данных NorthwindCS. Выделяем затем sqlDataAdapter1 на панели компонент, в окне Properties переходим по ссылке "Generate DataSet". Вводим имя "dsCustomers". В конструкторе формы добавляем код для заполнения элемента DataGrid данными при загрузке:

public Form1()
{
	InitializeComponent();
	sqlDataAdapter1.Fill(dsCustomers1);
	dataGrid1.DataSource = dsCustomers1.Tables["Customers"].DefaultView;
}

В классе формы создаем метод FindCustomers, в котором будет осуществляться обработкаДополнительные сведения о классе ArrayList см. в конце лекции.:

private void FindCustomers()
{
	//Создаем экземпляр filteringFields класса ArrayList
	ArrayList filteringFields = new ArrayList();
	//Если элемент fcbCustomerID доступен для поиска
	if (fcbCustomerID.SearchEnabled)
	//Добавляем в массив filteringFields значение текстового поля ColumnValue
	filteringFields.Add("CustomerID LIKE \'" +
	 fcbCustomerID.ColumnValue + "%\'");

	if (fcbCompanyName.SearchEnabled)
	filteringFields.Add("CompanyName LIKE \'" +
	 fcbCompanyName.ColumnValue + "%\'");
			
	if (fcbContactName.SearchEnabled)
	filteringFields.Add("ContactName LIKE \'" +
	 fcbContactName.ColumnValue + "%\'");
			
	if (fcbPhone.SearchEnabled)
	filteringFields.Add("Phone LIKE \'" +
	 fcbPhone.ColumnValue + "%\'");
			
	string filter = "";
	//Комбинируем введенные в текстовые поля значения. 
	//Для объединения используем логический оператор "ИЛИ"
	if (filteringFields.Count == 1)
	filter = filteringFields[0].ToString();
	else if (filteringFields.Count > 1)
	{
		for(int i = 0; i < filteringFields.Count - 1; i++)
		filter += filteringFields[i].ToString() + " OR ";
		//Для объединения полей в запросе используем логический оператор "И"	
		//for(int i = 0; i < filteringFields.Count - 1; i++)
		//filter += filteringFields[i].ToString() + " AND ";

		filter += filteringFields[filteringFields.Count - 1].ToString();
	}
	//Создаем экземпляр dvSearch класса DataView
	DataView dvSearch = new DataView(dsCustomers1.Customers);
	//Передаем свойству RowFilter объекта DataView скомбинированное значение filter
	dvSearch.RowFilter = filter; 
	dataGrid1.DataSource = dvSearch;	
}

Добавляем обработчик кнопки "Поиск":

private void btnSearch_Click(object sender, System.EventArgs e)
{
	try
	{
		FindCustomers();
	}
	catch(Exception ex)
	{
		MessageBox.Show(ex.Message);
	}
}

Запускаем приложение. При определении логического оператора "ИЛИ" в методе FindCustomers поисковый запрос выводит записи, содержащие хотя бы одно значение из введенных полей (рис. 9.19, А), при определении оператора "И" - запись, содержащую все эти значения (рис. 9.19, Б).

(рис 9.19) Готовое приложение "FindCustomers". А - Запрос с оператором "ИЛИ" возвращает две записи, содержащие в разных полях введенное значение. Б - Запрос с оператором "И" не возвращает ничего, поскольку в таблице нет ни одной записи, содержащей оба значения

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

Класс HashTable

Если вы подзабыли описание этого класса, давайте вспомним его. Hashtable - это структура данных, предназначенная для осуществления быстрого поиска. Это достигается за счет связывания ключа с каждым объектом, который сохраняется в таблице. Hashtable - это объект, в котором хранятся пары значений: так называемый ключ и само значение. Элементы каждой коллекции - и ключей ( Keys ), и значений ( Values ) - являются типом object, а это значит, что в качестве индекса элемента в привычном понимании теперь выступает не int, а именно object! Создайте новое консольное приложение и назовите его HashtableExample. Листинг этого приложения:

using System;
using System.Collections;

namespace HashtableExample
{
	
	class Statistics
	{
		public Hashtable AbonentList;
		public Statistics()	
		{
			AbonentList = new Hashtable();
		}
	}

class Abonent
{
	public string Name;
	public int Phone;
	public Abonent(string n, int p)
	{
		Name = n; Phone = p;
	}
}
class Class1
{
	[STAThread]
	static void Main(string[] args)
	{
		Abonent a1 = new Abonent("Иванов", 1234567);
		Abonent a2 = new Abonent("Николаев", 3216547);
		Abonent a3 = new Abonent("Андреева", 685472);
		Abonent a4 = new Abonent("Волков", 1234500);
		Abonent a5 = new Abonent("Кириллова", 3245637);
		Statistics myStatistics = new Statistics();
		myStatistics.AbonentList.Add(a1.Phone, a1.Name);
		myStatistics.AbonentList.Add(a2.Phone, a2.Name);
		myStatistics.AbonentList.Add(a3.Phone, a3.Name);
		myStatistics.AbonentList.Add(a4.Phone, a4.Name);
		myStatistics.AbonentList.Add(a5.Phone, a5.Name);

		Console.WriteLine(myStatistics.AbonentList[685472]);
	}
}
}

В методе Main создаются пять объектов класса Abonent, которые затем добавляются в Hashtable AbonentList ( myStatistics.AbonentList ) в коллекцию Values. Ключами для этих элементов будут служить значения их полей Phone. Обратите внимание, что метод Add() класса Hashtable требует два параметра: значение первого аргумента будет выступать в роли ключа для элемента, которым является значение второго аргумента.

Результатом выполнения программы будет вывод фамилии абонента, с заданным номером телефона (ключом) (рис. 9.20).

(рис 9.20) Приложение HashtableExample

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

Класс ArrayList

Класс ArrayList, подобно классу Hashtable, определенный в пространстве имен System.Collections, представляет собой один из чрезвычайно простых и удобных способов работы с наборами элементов. Объекты этого класса не имеют фиксированного размера и при необходимости могут менять его. Объект ArrayList при своем создании резервирует место в памяти для 16 элементов - указателей на тип object. При добавлении семнадцатого элемента размерность ArrayList увеличивается до 32 элементов. Обращение к объектам осуществляется аналогично обращению к элементам массива. Создайте новое консольное приложение, назовите его "ClassArrayList". В таблице 9.2 приводится полный его листинг:

Приложение ClassArrayList
Листинг приложения ClassArrayList Результат работы приложения (рис. 9.21)
using System;
using System.Collections;

namespace ClassArrayList
{
	class Class1
	{
		
		[STAThread]
		static void Main(string[] args)
		{
			ArrayList ar = new ArrayList();
			ar.Add("A");
			ar.Add("AB");
			ar.Add("ABC");
			ar.Add("ABCD");
			ar.Add("ABCDE");
			ar.Add("ABCDEF");
			ar.Add("ABCDEFG");
			ar.Add("ABCDEFGH");
			ar.Add("");
			ar.Add("");
			
			Console.WriteLine("Вывод
			 элементов массива:\n");
			foreach (object element in ar)
			{
				Console.WriteLine(element);
			}
			ar.Remove("ABCD");
			Console.WriteLine("Удаление
			 элемента:\n");
			foreach (object element in ar)
			{
				Console.WriteLine(element);
			}
			ar.Insert(6, "XYZ");
			Console.WriteLine("Вставка элемента \n
			 на заданную позицию:\n");
			foreach (object element in ar)
			{
				Console.WriteLine(element);
			}
			ar.Clear();
			Console.WriteLine("Удаление
			 всех элементов:\n");
			foreach (object element in ar)
			{
				Console.WriteLine(element);
			}
		}
	}
}
(рис 9.21)

Из рис. 9.21 видно, что элементами ArrayList могут быть любые значения, поскольку он содержит в себе указатели на тип object. Для возвращения значений из массива необходимо осуществлять явное преобразование типов.

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

Страницы:
Внимание! Для работы с лекциями 9, 10 необходимы учебные файлы, которые Вы можете загрузить здесь.

Объект DataView. Вывод связанных таблиц

Объект DataView. Фильтрация и сортировка данных

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

Скопируйте папку приложения VisualDataSQL из второй лекции и переименуйте ее в VisualDataView. Открываем проект и перетаскиваем на форму еще один элемент DataGrid, свойству Dock которого устанавливаем значение Bottom. Выделив добавленный элемент DataGrid, перетащим на форму элемент управления Splitter (разделитель), свойству Dock которого также установим значение Bottom. В результате на форме располагаются две таблицы, в режиме запуска размеры покрытия их можно будет изменять, передвигая разделитель. Переключаемся на вкладку Data панели инструментов Toolbox и пертаскиваем на форму элемент DataView (рис. 9.1).

(рис 9.1) Элемент управления DataView

Добавленный объект появляется на панели компонент. Прежде всего следует определить, что будет представлять источник данных для него. Переходим в окно Properties, в свойстве Table выбираем таблицу Customers объекта DataSet (рис. 9.2).

(рис 9.2) Определение таблицы объекта DataView

Теперь определим фильтр, который будет определять содержимое DataView. Выбираем свойство RowStateFilter, нажимаем на кнопку "Original Rows" (рис. 9.3, A), затем снимаем все галочки, кроме "Deleted" (рис. 9.3, Б).

(рис 9.3) Окно свойств объекта DataView

Кнопка "Original Rows" переключает на свойства первоначальных (оригинальных), исходных записей, загружаемых в DataSet. Выбрав пункт "Deleted", мы тем самым отобрали фильтр для удаленных записей.

В окне Properties второго элемента DataGrid в свойстве DataSource устанавливаем объект DataView в качестве источника данных (рис. 9.4).

(рис 9.4) Определение объекта DataView в качестве источника данных для объекта DataGrid

Запускаем приложение. Выделяем записи поля CustomerID со значениями "ANTON" и "BLAUS" и удаляем их - они немедленно появляются во втором элементе DataGrid (рис. 9.5).

(рис 9.5) Удаленные записи оказываются во второй таблице

Изменим фильтр - в поле свойства RowStateFilter нажимаем на кнопку "Current Rows" (Текущие записи) и оставляем галочки на пунктах "New" и "Current Modified" (рис. 9.6):

(рис 9.6) Установка нового фильтра

Запускаем приложение. Теперь во вторую таблицу будут помещаться текущие записи, в которые были внесены изменения - в первой записи в поле City было добавлено "123", а также новые записи - прокручиваем список до конца и вводим данные (рис. 9.7).

(рис 9.7) Применение фильтра по новым и измененным записям

Каждый объект DataSet поддерживает две версии - исходную, полученную при загрузке из базы данных, и текущую, в которую были внесены изменения. Данные, выведенные на форму в элемент управления DataGrid, можно изменять, при этом фиксируется статус записей объекта DataRow с помощью свойства RowState. Свойство RowStateFilter осуществляет фильтрацию по этому свойству, причем кнопке "Original" соответствуют исходные данные, а кнопке "Current" - текущие. Значения этого свойства приведены в таблице 9.1.

Значения свойства RowStateFilter объекта DataView
Свойство Описание
Unchanged Записи без изменений
New Новые записи
Deleted Удаленные записи
Current Modified Измененные записи с их текущими значениями
Original Modified Измененные записи с их первоначальными значениями

Как мы уже видели, для одного объекта DataView в пределах одной версии объекта DataSet (исходной или текущей) возможно объединение фильтров - например, отображение новых и удаленных записей.

Свойство RowFilter предназначено для вывода записей, содержащих определенное значение заданного поля. Установим свойству RowStateFilter значение по умолчанию - CurrentRows, - а затем в свойстве RowFilter введем фильтрацию (рис. 9.8):

City = 'London'
(рис 9.8) Свойство RowFilter объекта DataView

Запускаем приложение. Во второй таблице выводятся все записи с названием города "London" (рис. 9.9, А). Установив в свойстве RowFilter другое значение - ContactName = 'Thomas Hardy', - получаем одну запись, содержащую это значение (рис. 9.9, Б):

(рис 9.9) Вывод записей со значением "London" поля City (А) и записи со значением "Thomas Hardy поля ContactName (Б)

Свойство Sort предназначено для вывода записей в порядке возрастания (ascending, ASC) или убывания (descending, DESC) по значениям заданного поля. В принципе, элемент DataGrid сам по себе поддерживает сортировку - достаточно просто щелкнуть по заголовку поля. Однако это требует действий от пользователя, тогда как объект DataView может предоставлять данные уже в готовом виде.

Удаляем значение свойства RowFilter, в поле свойства Sort вводим "City ASC" (рис. 9.10):

(рис 9.10) Сортировка по полю City

Запускаем приложение. Во второй таблице отображаются записи со значениями поля City в порядке возрастания (рис. 9.11, А). Введем другое значение поля Sort - "ContactName DESC" - в результате получаем сортировку записей в порядке убывания по полю CompanyName (рис. 9.11, Б):

(рис 9.11) Сортировка записей по полю City (А), сортировка записей по полю ContactName (Б)

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

Приступим теперь к рассмотрению этих же свойств объекта DataView, создаваемых программным образом. Скопируйте папку приложения ProgrammDataSQL и переименуйте ее в ProgrammDataView. Добавляем на форму второй DataGrid и Splitter точно так же, как мы это сделали в предыдущем приложении. Переходим в конструктор формы и изменяем код следующим образом:

public Form1()
{
	InitializeComponent();
	SqlDataAdapter dataAdapter = new SqlDataAdapter(CommandText, ConnectionString);
	DataSet ds = new DataSet();
	//Создаем экземпляр dtCustomer объекта DataTable
	DataTable dtCustomer = ds.Tables.Add("Customer");
	dataAdapter.Fill(ds, "Customer");
	dataGrid1.DataSource = ds.Tables["Customer"].DefaultView;
	//Создаем экземпляр myDataView объекта DataView,
	//передаем ему объект dtCustomer
	DataView myDataView = new DataView(dtCustomer);
	//Устанавливаем фильтр для удаленных записей
	myDataView.RowStateFilter = DataViewRowState.Deleted;
	//Для второго элемента DataGrid указываем myDataView
	//в качестве источника данных.
	dataGrid2.DataSource = myDataView;
}

Объект DataView представляет данные объекта DataTable - именно поэтому нам пришлось создать экземпляр dtCustomer. Запускаем приложение и проверяем его функциональность - результат должен быть в точности такой же, как и ранее.

Для вывода новых строк (DataViewRowState.Added) и (символ "|" ) измененных (DataViewRowState.ModifiedCurrent) фильтр будет выглядеть так:

myDataView.RowStateFilter =
 ((DataViewRowState)((DataViewRowState.Added | DataViewRowState.ModifiedCurrent)));

Закомментируем в коде свойство RowStateFilter. Для вывода фильтров по свойству RowFilter используем следующие фрагменты кода:

//Выводим все записи со значением 'London' поля City:
myDataView.RowFilter = "City = 'London'";

Или

//Выводим все записи со значением 'Thomas Hardy' поля ContactName:
myDataView.RowFilter = "ContactName = 'Thomas Hardy'";

Закомментируем после проверки в коде эти фрагменты и добавим сортировку записей:

// Сортируем записи по полю "City" в порядке возрастания
myDataView.Sort = "City ASC";

Или

//Сортируем записи по полю ContactName в порядке убывания
myDataView.Sort = "ContactName DESC";

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

Вывод связанных таблиц. Вывод двух таблиц в один элемент DataGrid

База данных Microsoft Access BDTur_firm.mdb содержит таблицу "Туристы", которая связана с другими таблицами. Было бы удобно выводить эту таблицу в элемент DataGrid вместе с другими таблицами, а также выводить связанные записи этой таблицы. Создайте новое Windows-приложение и назовите его DataGrid2Table. Перетаскиваем на форму элемент управления DataGrid, свойству Dock устанавливаем значение Fill. Переходим в код формы и подключаем пространство имен:

using System.Data.OleDb;

В конструкторе формы создаем соединение, объект OleDbCommand, определяем для него соединение и строку CommandText:

OleDbConnection conn = new OleDbConnection(connectionString);
OleDbCommand myCommand = new OleDbCommand();
myCommand.Connection = conn;
myCommand.CommandText = commandText;

Подключаемся к файлу базы данных BDTur_firm.mdb, указываем соответствующие параметры строк connectionString и commandText:

string commandText = "SELECT [Код туриста], Фамилия, Имя, Отчество FROM Туристы";
string connectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0"
 ";Data Source=""D:\Uchebnik\Code\Glava1\BDTur_firm.mdb"";
 ID=Admin;Jet OLEDB:Encrypt Database=False";

Создаем объект DataAdapter и в свойстве SelectCommand устанавливаем значение myCommand, открываем соединение:

OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
dataAdapter.SelectCommand = myCommand;
conn.Open();

Создаем объект DataSet:

DataSet ds = new DataSet();

В объекте DataSet здесь будут храниться две таблицы - главная и связанная с ней дочерняя. Поэтому воспользуемся свойством TableMappings объекта DataAdapter для занесения в него первой таблицы "Туристы":

DataSet ds = new DataSet();
dataAdapter.TableMappings.Add("Table", "Туристы");
dataAdapter.Fill(ds);

Свойству DataSource объекта dataGrid1 указываем таблицу "Туристы" объекта ds. Обратите внимание на синтаксис - свойство Tables подразумевает наличие нескольких таблиц в объекте DataSet:

dataGrid1.DataSource = ds.Tables["Туристы"].DefaultView;

Закрываем соединение:

conn.Close();

Запускаем приложение. Пока на форме появляется только одна таблица. Закрываем приложение и переходим в код формы. Теперь нам следует добавить объекты OleDbDataAdapter и OleDbCommand для таблицы "Информация о туристах":

OleDbCommand myCommand2 = new OleDbCommand();
myCommand2.Connection = conn;
myCommand2.CommandText = commandText2;
OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();

Обратите внимание, что dataAdapter2 использует то же самое подключение conn, что и dataAdapter.

Строку commandText2 определим следующим образом:

string commandText2 = "SELECT [Код туриста], [Серия паспорта],
 Город, Страна, Телефон, Индекс FROM [Информация о туристах]";

Теперь свяжем второй объект OleDbDataAdapter с только что созданной второй командой и отобразим "Информацию о туристах" на его таблицу. Затем можно заполнить объект DataSet данными из второй таблицы:

dataAdapter2.SelectCommand = myCommand2;
dataAdapter2.TableMappings.Add("Table", "Информация о туристах");
dataAdapter2.Fill(ds);

В итоге у нас получился объект DataSet с двумя таблицами. Теперь можно выводить одну из этих таблиц на форму, или две сразу. Но связь между таблицами еще не создана. Для конфигурирования отношения по полю "Код туриста" создаем два объекта DataColumn:

DataColumn dcTouristsID =
 ds.Tables["Туристы"].Columns["Код туриста"];
DataColumn dcInfoTouristsID =
 ds.Tables["Информация о туристах"].Columns["Код туриста"];

Создаем объект DataRelation, в его конструкторе передаем название отношения между таблицами и два объекта DataColumn:

DataRelation dataRelation =
 new DataRelation("Дополнительная информация", dcTouristsID, dcInfoTouristsID);

Добавляем созданный объект отношения к объекту DataSet:

ds.Relations.Add(dataRelation);

Создаем объект DataViewManager, отвечающий за отображение DataSet в объекте DataGrid:

DataViewManager dsview = ds.DefaultViewManager;

Присваиваем свойству DataSource объекта DataGrid созданный объект DataViewManager:

dataGrid1.DataSource = dsview;

Последнее, что нам осталось сделать, - сообщить объекту DataGrid, какую таблицу считать главной (родительской) и, соответственно, отображать на форме:

dataGrid1.DataMember = "Туристы";

Закрываем соединение:

conn.Close();

Запускаем приложение. Теперь можно просматривать связанные записи (рис. 9.12):

(рис 9.12) Для перехода на дочернюю запись нажимаем на ссылку "Дополнительная информация". Для возвращения на родительскую запись нажимаем на кнопку со стрелкой

Обратите внимание, что здесь мы не стали накладывать ограничения - это сделано для упрощения кода.

Конструктор формы будет иметь следующий вид:

public Form1()
{
	InitializeComponent();
	OleDbConnection conn = new OleDbConnection(connectionString);
	OleDbCommand myCommand = new OleDbCommand();
	myCommand.Connection = conn;
	myCommand.CommandText = commandText;
	OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
	dataAdapter.SelectCommand = myCommand;
	conn.Open();
			
	DataSet ds = new DataSet();
	dataAdapter.TableMappings.Add("Table", "Туристы");
	dataAdapter.Fill(ds);
		
	OleDbCommand myCommand2 = new OleDbCommand();
	myCommand2.Connection = conn;
	myCommand2.CommandText = commandText2;
	OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();
	dataAdapter2.SelectCommand = myCommand2;
	dataAdapter2.TableMappings.Add("Table", "Информация о туристах");
	dataAdapter2.Fill(ds);
		
	DataColumn dcTouristsID = ds.Tables["Туристы"].Columns["Код туриста"];
	DataColumn dcInfoTouristsID =
	 ds.Tables["Информация о туристах"].Columns["Код туриста"];
	DataRelation dataRelation =
	 new DataRelation("Дополнительная информация", dcTouristsID, dcInfoTouristsID);
	ds.Relations.Add(dataRelation);
	DataViewManager dsview = ds.DefaultViewManager;
	dataGrid1.DataSource = dsview;
	dataGrid1.DataMember = "Туристы";
	conn.Close();
}

Закомментируем фрагмент кода, отвечающий за создание связи:

//DataColumn dcTouristsID = ds.Tables["Туристы"].Columns["Код туриста"];
//DataColumn dcInfoTouristsID =
// ds.Tables["Информация о туристах"].Columns["Код туриста"];
//DataRelation dataRelation =
// new DataRelation("Дополнительная информация", dcTouristsID, dcInfoTouristsID);
//ds.Relations.Add(dataRelation);
DataViewManager dsview = ds.DefaultViewManager;
dataGrid1.DataSource = dsview;
//dataGrid1.DataMember = "Туристы";
conn.Close();

Теперь в приложении будут отдельные ссылки на две таблицы - "Туристы" (рис. 9.13, А) и "Информация о туристах" (рис. 9.13, Б):

(рис 9.13) Переход на две таблицы - "Туристы" (А) и "Информация о туристах" (Б)

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

Вывод связанных таблиц в два элемента DataGrid1

Наиболее часто встречаемая задача при разработке приложений, связанных с базами данных: одновременный вывод двух таблиц на форму, причем при перемещении по записям главной таблицы в дочерней автоматически отображаются связанные записи. Мы сделаем два приложения - одно будет создано с использованием визуальных инструментов студии, другое - полностью программно. Создайте новый Windows-проект и назовите его Visual2DataGrid2Table. Из окна Toolbox перетаскиваем на форму элемент DataGrid, свойству Dock этого элемента устанавливаем значение Bottom. Добавляем элемент Splitter, его свойству Dock также устанавливаем значение Bottom. Наконец, перетаскиваем второй DataGrid, свойству Dock устанавливаем значение Fill. Дополнительно определяем следующие свойства элементов:

dataGrid1, свойство Значение
Name dgInfoTourists
CaptionText Информация о туристах
dataGrid2, свойство Значение
Name dgTourists
CaptionText Туристы

Переходим на вкладку Data окна Toolbox, перетаскиваем на форму элемент (...). В редакторе Tables Collection Editor нажимаем кнопку "Add" для добавления таблицы "Туристы" (TableName - "Туристы", Name - "dtTourists"). В поле свойства (...) и создаем следующие столбцы:

Поле Column1, свойство Значение
ColumnName Код туриста
DataType System.Int32
Unique True
Name dcTouristID
Поле Column2, свойство Значение
ColumnName Фамилия
Name dcLastName
Поле Column3, свойство Значение
ColumnName Имя
Name dcFirstName
Поле Column4, свойство Значение
ColumnName Отчество
Name dcMiddleName

Аналогично, в редакторе Tables Collection Editor создаем таблицу "Информация о туристах", а затем в редакторе Columns Collection Editor создаем ее поля:

TableName Информация о туристах
Name dtInfoTourists
Поле Column1, свойство Значение
ColumnName Код туриста
DataType System.Int32
Unique True
Name dcInfoTouristsID
Поле Column2, свойство Значение
ColumnName Серия паспорта
Name dcPassport
Поле Column3, свойство Значение
ColumnName Город
Name dcCity
Поле Column4, свойство Значение
ColumnName Страна
Name dcCountry
Поле Column5, свойство Значение
ColumnName Телефон
Name dcPhone
Поле Column6, свойство Значение
ColumnName Индекс
Name dcIndex

Завершив настройку таблиц и колонок, переходим к определению связи между таблицами. Выделяем объект (...). В появившемся редакторе Relation Collection Editor нажимаем кнопку "Add" и создаем отношение "Дополнительная_информация" между таблицами по ключевому полю "Код туриста". Завершив работу с редактором, переходим в код формы. Подключаем пространство имен для работы с базой данных Microsoft Access:

using System.Data.OleDb;

Добавляем строки для подключения к базе и извлечения данных:

string connectionString = @"Provider=""Microsoft.Jet.OLEDB.4.0"";
 Data Source=""D:\Uchebnik\Code\Glava1\BDTur_firm.mdb"";
 User ID=Admin;Jet OLEDB:Encrypt Database=False";
string commandText = "SELECT [Код туриста], Фамилия, Имя, Отчество FROM Туристы";
string commandText2 = "SELECT [Код туриста], [Серия паспорта],
 Город, Страна, Телефон, Индекс FROM [Информация о туристах]";

В конструкторе формы создаем объекты для заполнения данными DataSet:

public Form1()
		{
			
			InitializeComponent();

			OleDbConnection conn = new OleDbConnection(connectionString);
			OleDbCommand myCommand = new OleDbCommand();
			myCommand.Connection = conn;
			myCommand.CommandText = commandText;
			OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
			dataAdapter.SelectCommand = myCommand;

			OleDbCommand myCommand2 = new OleDbCommand();
			myCommand2.Connection = conn;
			myCommand2.CommandText = commandText2;
			OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();
			dataAdapter2.SelectCommand = myCommand2;
		
			conn.Open();
			dataAdapter.Fill(dsTourists.Tables["Туристы"]);
			dataAdapter2.Fill(dsTourists.Tables["Информация о туристах"]);
			conn.Close();
	
			dgTourists.DataSource = dsTourists;
			dgTourists.DataMember = "Туристы";

			dgInfoTourists.DataSource = dsTourists;
			dgInfoTourists.DataMember = "Туристы.Дополнительная_информация";
		}

Запускаем приложение (рис. 9.14):

(рис 9.14) Готовое приложение Visual2DataGrid2Table. При перемещении по записям таблицы "Туристы" автоматически выводятся связанные записи в таблице "Информация о туристах"

Ключевым моментом здесь является определение связи "Дополнительная_информация" в содержимом элемента dgInfoTourists:

dgInfoTourists.DataMember = "Туристы.Дополнительная_информация";

Эта же связь доступна и в окне родительской таблицы - при нажатии на гиперссылку возвращается связанная запись.

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

Рассмотрим теперь программную реализацию этого же приложения. Создайте новый Windows-проект и назовите его "Programm2DataGrid2Table". Скопируем из проекта Visual2DataGrid2Table два элемента DataGrid, Splitter и вставим в новую форму. Пространство имен, строки connectionString и commandText будут в точности такие же, как и в предыдущем проекте, - просто скопируйте их. Конструктор формы будет иметь следующий вид:

public Form1()
{
	InitializeComponent();
	// Создаем объект dtTourists для таблицы "Туристы"
	DataTable dtTourists = new DataTable("Туристы");
	DataColumn dcTouristID = new DataColumn("Код туриста", typeof(int));
	dcTouristID.Unique = true;
	DataColumn dcLastName = new DataColumn("Фамилия",typeof(string));
	DataColumn dcFirstName = new DataColumn("Имя", typeof(string));
	DataColumn dcMiddleName = new DataColumn("Отчество", typeof(string));
	dtTourists.Columns.AddRange(new DataColumn[] {
		dcTouristID, 
		dcLastName, 
		dcFirstName, 
		dcMiddleName });
	// Создаем объект dtInfoTourists для таблицы "Информация о туристах"
	DataTable dtInfoTourists = new DataTable("Информация о туристах");
	DataColumn dcInfoTouristsID = new DataColumn("Код туриста", typeof(int));
	dcInfoTouristsID.Unique = true;
	DataColumn dcPassport = new DataColumn("Серия паспорта", typeof(string));
	DataColumn dcCity = new DataColumn("Город", typeof(string));
	DataColumn dcCountry = new DataColumn("Страна", typeof(string));
	DataColumn dcPhone	= new DataColumn("Телефон", typeof(decimal));
	DataColumn dcIndex	= new DataColumn("Индекс", typeof(decimal));
					
	dtInfoTourists.Columns.AddRange(new DataColumn[] {
		dcInfoTouristsID,
		dcPassport,
		dcCity,
		dcCountry,
		dcPhone, 
		dcIndex });

	DataSet dsTourists = new DataSet();
	// Добавляем таблицы в DataSet
	dsTourists.Tables.AddRange(new DataTable[] {
		dtTourists, 
		dtInfoTourists});		

	// Создаем отношение между таблицами
	dsTourists.Relations.Add(new DataRelation("Дополнительная_информация", 
		dcTouristID,
		dcInfoTouristsID));
			
	//Подключаемся к базе и выводим данные
	OleDbConnection conn = new OleDbConnection(connectionString);
	OleDbCommand myCommand = new OleDbCommand();
	myCommand.Connection = conn;
	myCommand.CommandText = commandText;
	OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
	dataAdapter.SelectCommand = myCommand;

	OleDbCommand myCommand2 = new OleDbCommand();
	myCommand2.Connection = conn;
	myCommand2.CommandText = commandText2;
	OleDbDataAdapter dataAdapter2 = new OleDbDataAdapter();
	dataAdapter2.SelectCommand = myCommand2;
	
	dataAdapter.Fill(dsTourists.Tables["Туристы"]);
	dataAdapter2.Fill(dsTourists.Tables["Информация о туристах"]);
	dgTourists.DataSource = dsTourists;
	dgTourists.DataMember = "Туристы";

	dgInfoTourists.DataSource = dsTourists;
	dgInfoTourists.DataMember = "Туристы.Дополнительная_информация";
}

Запускаем приложение - его функциональность должна быть в точности такая же, как и в проекте Visual2DataGrid2Table.

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

Свойство PrimaryKey

Мы научились конструировать структуру таблицы в объекте DataSet, а также определять отношения между таблицами. Во всех случаях для выделения первичного ключа в таблице использовалось свойство Unique. Например, в самом последнем примере - проекте Programm2DataGrid2Table - первичный ключ "Код туриста" определялся так:

...
DataColumn dcTouristID = new DataColumn("Код туриста", typeof(int));
dcTouristID.Unique = true;
...

DataColumn dcInfoTouristsID = new DataColumn("Код туриста", typeof(int));
dcInfoTouristsID.Unique = true;
...

Для вывода таблиц идентификации записей этого определения вполне хватало. Однако свойство Unique всего лишь указывает на уникальность заданного поля, т.е. на отсутствие повторяющихся записей. В самом деле, в таблице может быть несколько полей, которые должны быть уникальными и одно из них (или их комбинация) будут образовывать первичный ключ. Для указания именно первичного ключа используется свойство PrimaryKey объекта DataTable:

DataTable dtTourists = new DataTable("Туристы");
DataColumn dcTouristID = new DataColumn("Код туриста", typeof(int));
dtTourists.PrimaryKey = new DataColumn [] {dtTourists.Columns["Код туриста"]};

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

dtTourists.Columns.Add("Код туриста", typeof(int));
dtTourists.PrimaryKey = new DataColumn [] {dtTourists.Columns["Код туриста"]};

Можно применять комбинацию полей для задания первичного ключа:

DataTable dtTourists = new DataTable("Туристы");
DataColumn dcTouristID =
 new DataColumn("Код туриста", typeof(int));
DataColumn dcLastName =
 new DataColumn("Фамилия",typeof(string));
dtTourists.PrimaryKey =
 new DataColumn [] {dtTourists.Columns["Код туриста"],
 dtTourists.Columns["Фамилия"]};

Здесь первичным ключом будут значения поля "Код туриста" в сочетании со значением поля "Фамилия".

При конструировании объекта DataTable в редакторе Tables Collection Editor после создания объектов Columns в свойстве PrimaryKey можно выделять одно или несколько полей - они отмечаются числами и становятся первичным ключом (рис. 9.15):

(рис 9.15) Определение первичного ключа в редакторе Tables Collection Editor

После определения первичного ключа объекта DataTable для свойства AllowDBNull (разрешение значений null) объектов DataColumn, формирующих ключ, будет установлено значение false.

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

Поиск данных

Технология ADO .NET предоставляет значительные возможности для поиска данных - такие объекты, как DataSet, DataTable, содержат специализированные методы для быстрого решения этой задачи. Между тем свойство RowFilter объекта DataView может применяться для создания простого и эффективного поиска. Запускаем Visual Studio .NET и создаем новый проект. В окне New Project выбираем тип Windows Control Library, называем его "FindControl" (рис. 9.16):

(рис 9.16) Выбор шаблона Windows Control Library

Появляется форма пользовательского (композитного) элемента управления. Устанавливаем следующие свойства:

UserControl1, свойство Значение
Name FindCheckBox
Size 230; 70

В окне Solution Explorer изменяем название "UserControl1.cs" на "FindCheckBox.cs". Перетаскиваем на форму из окна Toolbox текстовое поле, надпись и элемент CheckBox. Настраиваем их свойства:

CheckBox1, свойство Значение
Name chbForSearching
Location 8; 32
Text
textBox1, свойство Значение
Name txtColumnValue
Location 32; 32
Size 184; 20
Text
label1, свойство Значение
Name lblColumnName
Location 40; 8
Text ColumnName

Переходим к коду. В конструкторе формы отключаем доступность текстового поля и привязываем обработчик события CheckedChanged для элемента CheckBox:

public FindCheckBox()
{
	InitializeComponent();
	txtColumnValue.Enabled = false;
	chbForSearching.CheckedChanged +=
	 new EventHandler(chbForSearching_CheckedChanged);
}

В классе формы создаем обработчик события CheckedChanged для CheckBox и определяем действия для значений текстового поля, надписи и элемента CheckBox:

private void chbForSearching_CheckedChanged(object sender, EventArgs e)
{
	txtColumnValue.Enabled = chbForSearching.Checked;
}
[Category("Appearance"),
 Description("Название поля, по которому будет осуществляться поиск")]
public string ColumnName
{
	get {return lblColumnName.Text;}
	set {lblColumnName.Text = value;}
}
[Category("Appearance"), Description("Ключевое слово для поиска")]
public string ColumnValue
{
	get {return txtColumnValue.Text;}
	set {txtColumnValue.Text = value;}
}
		
[Category("Appearance"), Description("Включение поиска по заданному полю")]
public bool SearchEnabled
{
	get {return chbForSearching.Checked;}
	set {chbForSearching.Checked = value;}
}

Свойства ColumnName, ColumnValue и SearchEnabled будут свойствами композитного элемента управления, которые будут отображаться в его окне Properties. В квадратных скобках указан атрибут для помещения свойства в заданную группу и описание, выводимое на информационную панель. Компилируем приложение (Ctrl+Shift+B) и закрываем его. Создаем новое Windows-приложение, называем его "FindCustomers". Перетаскиваем на форму следующие элементы управления: Panel (свойству Dock устанавливаем значение Left ), Splitter и DataGrid (свойству Dock устанавливаем значение Fill ). В окне Toolbox щелкаем правой кнопкой мыши на вкладке Windows Forms и в появившемся контекстном меню выбираем пункт "Add \ Remove Items_". В появившемся окне Customize Toolbox на вкладке .NET Framework Components нажимаем кнопку "Browse" (рис. 9.17, А) и переходим в директор ию, где расположена сборка композитного элемента FindControl (у меня это D:\Uchebnik\Code\Glava4\FindControl\bin\Debug, сборка FindControl.dll) (рис. 9.17, Б). После выбора сборки она появляется в окне Customize Toolbox (рис. 9.17, В).

(рис 9.17) Добавление композитного элемента. А - окно "Customize Toolbox", Б - Выбор сборки FindControl.dll, В - добавленная сборка на вкладке ".NET Framework Components"

Нажимаем кнопку "OK" и перетаскиваем на панель формы из окна Toolbox четыре копии элемента FindCheckBox. Добавляем также кнопку поиска (рис. 9.18). Устанавливаем следующие свойства элементов:

findCheckBox1, свойство Значение
ColumnName CustomerID
Name fcbCustomerID
findCheckBox2, свойство Значение
ColumnName CompanyName
Name fcbCompanyName
findCheckBox3, свойство Значение
ColumnName ContactName
Name FcbContactName
findCheckBox4, свойство Значение
ColumnName Phone
Name fcbPhone
button1, свойство Значение
Name btnSearch
Text Поиск
(рис 9.18) Расположение элементов на форме. Окно Properties композитного элемента

Обратите внимание (рис. 9.18), что в окне Properties, при групповом расположении, свойство ColumnName находится в группе Appearance. На информационную панель выводится описание этого свойства на русском языке. Именно эти параметры мы и указывали при создании композитного элемента.

Для того чтобы упростить код, создадим подключение к базе данных при помощи визуальных средств студии. В окне Toolbox переходим на вкладку Data, перетаскиваем на форму SqlDataAdapter и настраиваем извлечение таблицы Customers базы данных NorthwindCS. Выделяем затем sqlDataAdapter1 на панели компонент, в окне Properties переходим по ссылке "Generate DataSet". Вводим имя "dsCustomers". В конструкторе формы добавляем код для заполнения элемента DataGrid данными при загрузке:

public Form1()
{
	InitializeComponent();
	sqlDataAdapter1.Fill(dsCustomers1);
	dataGrid1.DataSource = dsCustomers1.Tables["Customers"].DefaultView;
}

В классе формы создаем метод FindCustomers, в котором будет осуществляться обработкаДополнительные сведения о классе ArrayList см. в конце лекции.:

private void FindCustomers()
{
	//Создаем экземпляр filteringFields класса ArrayList
	ArrayList filteringFields = new ArrayList();
	//Если элемент fcbCustomerID доступен для поиска
	if (fcbCustomerID.SearchEnabled)
	//Добавляем в массив filteringFields значение текстового поля ColumnValue
	filteringFields.Add("CustomerID LIKE \'" +
	 fcbCustomerID.ColumnValue + "%\'");

	if (fcbCompanyName.SearchEnabled)
	filteringFields.Add("CompanyName LIKE \'" +
	 fcbCompanyName.ColumnValue + "%\'");
			
	if (fcbContactName.SearchEnabled)
	filteringFields.Add("ContactName LIKE \'" +
	 fcbContactName.ColumnValue + "%\'");
			
	if (fcbPhone.SearchEnabled)
	filteringFields.Add("Phone LIKE \'" +
	 fcbPhone.ColumnValue + "%\'");
			
	string filter = "";
	//Комбинируем введенные в текстовые поля значения. 
	//Для объединения используем логический оператор "ИЛИ"
	if (filteringFields.Count == 1)
	filter = filteringFields[0].ToString();
	else if (filteringFields.Count > 1)
	{
		for(int i = 0; i < filteringFields.Count - 1; i++)
		filter += filteringFields[i].ToString() + " OR ";
		//Для объединения полей в запросе используем логический оператор "И"	
		//for(int i = 0; i < filteringFields.Count - 1; i++)
		//filter += filteringFields[i].ToString() + " AND ";

		filter += filteringFields[filteringFields.Count - 1].ToString();
	}
	//Создаем экземпляр dvSearch класса DataView
	DataView dvSearch = new DataView(dsCustomers1.Customers);
	//Передаем свойству RowFilter объекта DataView скомбинированное значение filter
	dvSearch.RowFilter = filter; 
	dataGrid1.DataSource = dvSearch;	
}

Добавляем обработчик кнопки "Поиск":

private void btnSearch_Click(object sender, System.EventArgs e)
{
	try
	{
		FindCustomers();
	}
	catch(Exception ex)
	{
		MessageBox.Show(ex.Message);
	}
}

Запускаем приложение. При определении логического оператора "ИЛИ" в методе FindCustomers поисковый запрос выводит записи, содержащие хотя бы одно значение из введенных полей (рис. 9.19, А), при определении оператора "И" - запись, содержащую все эти значения (рис. 9.19, Б).

(рис 9.19) Готовое приложение "FindCustomers". А - Запрос с оператором "ИЛИ" возвращает две записи, содержащие в разных полях введенное значение. Б - Запрос с оператором "И" не возвращает ничего, поскольку в таблице нет ни одной записи, содержащей оба значения

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

Класс HashTable

Если вы подзабыли описание этого класса, давайте вспомним его. Hashtable - это структура данных, предназначенная для осуществления быстрого поиска. Это достигается за счет связывания ключа с каждым объектом, который сохраняется в таблице. Hashtable - это объект, в котором хранятся пары значений: так называемый ключ и само значение. Элементы каждой коллекции - и ключей ( Keys ), и значений ( Values ) - являются типом object, а это значит, что в качестве индекса элемента в привычном понимании теперь выступает не int, а именно object! Создайте новое консольное приложение и назовите его HashtableExample. Листинг этого приложения:

using System;
using System.Collections;

namespace HashtableExample
{
	
	class Statistics
	{
		public Hashtable AbonentList;
		public Statistics()	
		{
			AbonentList = new Hashtable();
		}
	}

class Abonent
{
	public string Name;
	public int Phone;
	public Abonent(string n, int p)
	{
		Name = n; Phone = p;
	}
}
class Class1
{
	[STAThread]
	static void Main(string[] args)
	{
		Abonent a1 = new Abonent("Иванов", 1234567);
		Abonent a2 = new Abonent("Николаев", 3216547);
		Abonent a3 = new Abonent("Андреева", 685472);
		Abonent a4 = new Abonent("Волков", 1234500);
		Abonent a5 = new Abonent("Кириллова", 3245637);
		Statistics myStatistics = new Statistics();
		myStatistics.AbonentList.Add(a1.Phone, a1.Name);
		myStatistics.AbonentList.Add(a2.Phone, a2.Name);
		myStatistics.AbonentList.Add(a3.Phone, a3.Name);
		myStatistics.AbonentList.Add(a4.Phone, a4.Name);
		myStatistics.AbonentList.Add(a5.Phone, a5.Name);

		Console.WriteLine(myStatistics.AbonentList[685472]);
	}
}
}

В методе Main создаются пять объектов класса Abonent, которые затем добавляются в Hashtable AbonentList ( myStatistics.AbonentList ) в коллекцию Values. Ключами для этих элементов будут служить значения их полей Phone. Обратите внимание, что метод Add() класса Hashtable требует два параметра: значение первого аргумента будет выступать в роли ключа для элемента, которым является значение второго аргумента.

Результатом выполнения программы будет вывод фамилии абонента, с заданным номером телефона (ключом) (рис. 9.20).

(рис 9.20) Приложение HashtableExample

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

Класс ArrayList

Класс ArrayList, подобно классу Hashtable, определенный в пространстве имен System.Collections, представляет собой один из чрезвычайно простых и удобных способов работы с наборами элементов. Объекты этого класса не имеют фиксированного размера и при необходимости могут менять его. Объект ArrayList при своем создании резервирует место в памяти для 16 элементов - указателей на тип object. При добавлении семнадцатого элемента размерность ArrayList увеличивается до 32 элементов. Обращение к объектам осуществляется аналогично обращению к элементам массива. Создайте новое консольное приложение, назовите его "ClassArrayList". В таблице 9.2 приводится полный его листинг:

Приложение ClassArrayList
Листинг приложения ClassArrayList Результат работы приложения (рис. 9.21)
using System;
using System.Collections;

namespace ClassArrayList
{
	class Class1
	{
		
		[STAThread]
		static void Main(string[] args)
		{
			ArrayList ar = new ArrayList();
			ar.Add("A");
			ar.Add("AB");
			ar.Add("ABC");
			ar.Add("ABCD");
			ar.Add("ABCDE");
			ar.Add("ABCDEF");
			ar.Add("ABCDEFG");
			ar.Add("ABCDEFGH");
			ar.Add("");
			ar.Add("");
			
			Console.WriteLine("Вывод
			 элементов массива:\n");
			foreach (object element in ar)
			{
				Console.WriteLine(element);
			}
			ar.Remove("ABCD");
			Console.WriteLine("Удаление
			 элемента:\n");
			foreach (object element in ar)
			{
				Console.WriteLine(element);
			}
			ar.Insert(6, "XYZ");
			Console.WriteLine("Вставка элемента \n
			 на заданную позицию:\n");
			foreach (object element in ar)
			{
				Console.WriteLine(element);
			}
			ar.Clear();
			Console.WriteLine("Удаление
			 всех элементов:\n");
			foreach (object element in ar)
			{
				Console.WriteLine(element);
			}
		}
	}
}
(рис 9.21)

Из рис. 9.21 видно, что элементами ArrayList могут быть любые значения, поскольку он содержит в себе указатели на тип object. Для возвращения значений из массива необходимо осуществлять явное преобразование типов.

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

Вернуться к учебному плану