ку. УП 07. Практикум по курсу технологии баз данных Учебное пособие
Скачать 2.49 Mb.
|
Часть II. КЛИЕНТСКИЕ ТЕХНОЛОГИИ Очевидно, что конечный пользователь не знает языка SQL, поэтому ему нужно предоставить удобный и понятный пользовательский интерфейс, с по- мощью которого он мог бы формировать и получать данные. В архитектуре «клиент-сервер» СУБД представляется серверной стороной, а в качестве кли- ентского уровня может использоваться любое приложение (консольное, окон- ное, web-приложение), разработанное с помощью различных технологий и язы- ков программирования. Общая концепция обмена данными между клиентом и сервером по большей степени не зависит от технологии реализации клиентско- го приложения. Поэтому разберем вопросы, связанные с разработкой клиент- ского приложения на языке программирования C# с использованием техноло- гии ADO.NET. Приведем типовые примеры решения различных задач, возни- кающих на клиентской части приложения. Для подключения к базам данных в среде .NET Framework используется пространство имен System.Data и его подпространства имен (OleDb, Odbc и пр.). Разберем основные особенности работы элементов этого пространства имен. В основе любого из них – драйвер баз данных (коннектор) – специальное программное обеспечение, которое является посредником между клиентским приложением и базой данных (серверной или файловой). Для обращения ис- пользуются стандартные технологии Ole Db или ODBC. 2.1. ВЫПОЛНЕНИЕ ЗАПРОСА К БАЗЕ ДАНЫХ ИЗ КЛИЕНТСКОГО ПРИЛОЖЕНИЯ Простая модель обращения к базе данных реализуется через три основ- ных класса, которые могут быть реализованы на основе как универсальных технологий (Ole Db или ODBC), так и конкретных СУБД (например, SQL Server или MySQL): 63 Класс соединения; Класс команды; Класс курсора для получения данных из результата запроса. Класс соединения настраивает параметры базы данных, к которой следует подключиться. Открытие такого соединения позволяет формулировать коман- ды к серверу баз данных. Команды оформляются с помощью языка SQL и оформляются в программе с помощью класса команды. Класс курсора предна- значен для чтения данных результата запроса. Курсор обычно является после- довательным, т.е. с помощью него мы можем получить последовательно все за- писи результата запроса, начиная с первой до последней, но не можем вернуть- ся к уже просмотренным записям. Настройка соединения осуществляется через понятие строки соединения – параметров, которые описывают драйверы СУБД, конкретные базы данных и параметры безопасности для подключения к базе. Например, для настройки источника данных ODBC требуется воспользо- ваться средством администрирования операционной системы Windows. Рис. 37. Панель администрирования ОС Windows. Для общения с серверными СУБД требуется, чтобы источник данных был системным: 64 Рис. 38.Окно просмотра источников данных ODBC. При создании и редактировании источника данных требуется вы- брать драйвер и задать параметры подключения к базе данных: Рис. 39.Окно настройки источника данных для сервера MySQL. Драйверы могут устанавливаться и регистрироваться в операционной си- стеме вместе с соответствующей СУБД, а могут быть и самостоятельным ком- понентами, которые следует установить отдельно. Так, например, для SQL Server драйвер SQL Native Client устанавливается в процессе установки СУБД, а для сервера MySQL драйвер (MySQL ODBC Connector) является дополни- тельной компонентой, которую следует установить отдельно. Для настройки строки соединения через технологию Ole Db можно вос- пользоваться специальной утилитой операционной системы Windows. Для это- го требуется создать файл с расширением “.udl”. При последующем открытии этого файла будут вызвано окно настройки источника данных: 65 Рис. 40. Окно сервиса OLE DB Core Services. После выбора поставщика данных на вкладке «Подключение» следует настроить параметры базы данных (имя, местоположение, параметры безопас- ности). Этот файл можно будет открыть с помощью текстового редактора – в нем будет записана строка соединения через выбранный провайдер Ole Db. Для примера рассмотрим подключение к базе данных на SQL Server. Со- здадим файл connect.udl (например, с помощью «Блокнота») и откроем его с помощью службы OLE DB Core Services. На вкладке «Поставщик данных» сле- дует выбрать пункт «SQL Server Native Client номер версии» (номер версии, очевидно, зависит от установленного на компьютере SQL Server’а). Далее на вкладке «Соединение» следует настроить параметры подключения к серверу – имя сервера, параметры входа (обычно устанавливаются из системы безопасно- сти операционной системы), имя используемой базы данных. С помощью кноп- ки «Проверить подключение» можно протестировать созданную строку соеди- нения. 66 Рис. 41. Окно настройки параметров подключения к SQL Server. Далее при открытии файла connect.udl с помощью «Блокнота» мы увидим сгенерированную строку подключения: Provider=SQLNCLI11.1;Integrated Security=SSPI; Persist Security Info=False; User ID="";Initial Catalog=proba;Data Source=(local); Initial File Name="";Server SPN="" Далее приведен программный код, в котором осуществляется подключе- ние к источнику данных, заданному с помощью ODBC и получение данных из базы данных с помощью формирования SQL-запроса: // создание подключения к базе данных на основе строки соединения // с указанием источника данных ODBC OdbcConnection con = new OdbcConnection("DSN=proba"); // подключение к источнику данных con.Open(); // формирование команды SQL на выборку данных OdbcCommand com = new OdbcCommand("select * from Session", con); // выполнение команды на сервере и сохранение результата // в курсоре типа OdbcDataReader OdbcDataReader dr=com.ExecuteReader(); // переход к следующей строке посредством функции Read() // пока строки в результате есть – печатаем информацию из строк while (dr.Read()) Console.WriteLine(“”+dr[“NumGroup”]+” “+dr[“idSubject”]+ ” ”+dr[“Zach_exam”]); // закрывается соединение dr.Close(); con.Close(); 67 Аналогичным будет программный код и в случае использования подклю- чения по технологии Ole DB. Отличия будут только в именах используемых классов для доступа к базе данных (OleDbConnection, OleDbCommand, OleDbDataReader) и, соответственно, в пространстве имен, содержащее эти классы. Отметим еще момент, связанный с обеспечением доступа к полям строки курсора. Текущая строка представляется как ассоциативный массив, доступ к элементам которого можно осуществлять по номерам или по именам столбцов, что очень удобно с точки зрения последующего чтения программного кода. 2.2. ПАРАМЕТРЫ ЗАПРОСА Нередко запрос зависит от параметров, являющиеся значениями, по кото- рым осуществляется выборка или другие фрагменты запроса, например, усло- вия, формируемые программным образом. В этом случае используется коллек- ция параметров, которая имеется у объекта класса OdbcCommand. При созда- нии параметра в запросе указывается символ “?”, а далее добавляется применя- емое значение параметра в коллекцию параметров SQL-команды. В том случае, когда базовой технологией доступа является Ole Db, параметры в запросе име- нуются, причем эти имена начинаются с символа “@”. Пусть, например, номер группы, для которой требуется распечатать план сессии, вводится с клавиатуры и становится условием выборки записей из таб- лицы плана сессии для студентов. // создание подключения к базе данных на основе строки соединения // с указанием источника данных ODBC OdbcConnection con = new OdbcConnection("DSN=proba"); // подключение к источнику данных con.Open(); // ввод номера группы string group = Console.ReadLine(); // формирование команды SQL на выборку данных OdbcCommand com = new OdbcCommand ("select * from Sessions where NumGroup=?", con); 68 // задание значение параметра запроса com.Parameters.AddWithValue(“@par”,group); // выполнение команды на сервере и сохранение результата // в курсоре типа OdbcDataReader OdbcDataReader dr=com.ExecuteReader(); // переход к следующей строке посредством функции Read() // пока строки в результате есть – печатаем информацию из строк while (dr.Read()) Console.WriteLine(“”+dr[“NumGroup”]+” “+dr[“idSubject”]+ ” ”+dr[“Zach_exam”]); // закрывается соединение dr.Close(); con.Close(); Нередко использование параметров применяется при удаленном вызове хранимых процедур и функций. Вспомним, что у нас имеются функции, кото- рые позволяют перевести баллы в оценку (например, GetMark1). Вызов этой функции может быть таким: // создание подключения к базе данных на основе строки соединения // с указанием источника данных ODBC OdbcConnection con = new OdbcConnection("DSN=proba"); // подключение к источнику данных con.Open(); // ввод набранных баллов на экзамене string b = Console.ReadLine(); // формирование команды SQL на вызов функции и получение ее резуль- тата OdbcCommand com = new OdbcCommand("select GetMark1(?)", con); // задание значение параметра запроса com.Parameters.AddWithValue(“@ball”,b); // выполнение команды на сервере и сохранение результата //в курсоре типа OdbcDataReader OdbcDataReader dr=com.ExecuteReader(); // переход к первой строке – результат вызова функции dr.Read(); Console.WriteLine(“Оценка - ”+dr[0]); // закрывается соединение dr.Close(); con.Close(); 2.3. ВЫПОЛНЕНИЕ КОМАНД DML Напомним, что командами DML являются команды вставки новых запи- сей, изменения существующих записей и удаления записей. Данные команды возвращают число – количество строк, с которыми была выполнена требуемая 69 операция. Вызов этих команд из клиентского приложения отличается только функцией класса OdbcCommand (или OleDbCommand) – вместо ExecuteReader() вызывается функция ExecuteScalar(). Например, пусть создается новая учебная дисциплина: // создание подключения к базе данных на основе строки соединения // с указанием источника данных ODBC OdbcConnection con = new OdbcConnection("DSN=proba"); // подключение к источнику данных con.Open(); // ввод названия новой учебной дисциплины string title = Console.ReadLine(); // формирование команды SQL на добавление данных – в таблице ключ // задается с помощью поля-счетчика, так что указывать // его в запросе на вставку не обязательно OdbcCommand com = new OdbcCommand ("insert into Subjects values (‘?’)", con); // задание значение параметра запроса com.Parameters.AddWithValue(“@par”,title); // выполнение команды на сервере com.ExecuteScalar(); // закрывается соединение con.Close(); 2.4. ПОНЯТИЕ НАБОРА ДАННЫХ КАК ВИРТУАЛЬНОЙ БАЗЫ ДАННЫХ В пространстве имен System.Data определена система классов, которая реализует понятие набора данных. Набор данных (DataSet) представляет собой виртуальную базу данных, которая расположена в оперативной памяти и состо- ит из набора (коллекции) таблиц (объектов класса DataTable) и связей между ними (объектов класса DataRelation). Каждая таблица имеет набор столбцов (объектов типа DataColumn) и набор строк (объектов типа DataRow). Все таб- лицы в наборе данных хранятся в виде коллекции Tables. Столбцы в таблице хранятся тоже в виде коллекции под названием Columns, а строки – в виде кол- лекции под названием Rows. Обращение с таблицами набора данных напоми- нает работу с базой данных – у таблиц имеется функция Select(), которая задает параметры сортировки и выборки данных. Таким образом, можно моделировать различные запросы. 70 Набор данных можно создавать программно посредством создания объ- ектов-таблиц и столбцов. Сохранять информацию набора данных можно в фор- мате XML с помощью функции WriteXml(). Также возможна и загрузка инфор- мации из XML-файла с помощью функции ReadXml(). 2.5. СВЯЗЬ НАБОРА ДАННЫХ И БАЗЫ ДАННЫХ Набор данных может быть связан с реальными базами данных, реализо- ванными с помощью различных СУБД. Это реализуется с помощью объекта типа OdbcDataAdapter (или OleDbDataAdapter в зависимости от способа соеди- нения с базой данных). Создание SQL-команды осуществляется с помощью адаптера. Кроме того, класс-адаптер имеет специальный метод, который позво- ляет записать результат выполнения запроса в таблицу набора данных. Приведем пример заполнения набора данных с помощью адаптера: // создание подключения к базе данных на основе строки соединения // с указанием источника данных ODBC OdbcConnection con = new OdbcConnection("DSN=proba"); // подключение к источнику данных con.Open(); // формирование адаптера для связи с базой данных OdbcDataAdapter adapt = new OdbcDataAdapter ("select * from Sessions", con); // создание набора данных для сохранения результата запроса DataSet ds = new DataSet(); // заполнение таблицы набора данных (второй параметр) // из базы данных через адаптер adapt.Fill(ds,”Sessions”); // перебор строк из полученной таблицы Sessions набора данных // имена столбцов в таблице набора данных будут теми же, // что и в исходной таблице базы данных foreach (DataRow dr in ds.Tables[“Sessions”]) Console.WriteLine(“”+dr[“NumGroup”]+” “+dr[“idSubject”]+ ” ”+dr[“Zach_exam”]); con.Close(); Отметим, что, так как набор данных является уже отсоединенным от базы данных, можно повторно просматривать элементы данных в любом порядке. 71 2.6. КАК СИНХРОНИЗИРОВАТЬ ИЗМЕНЕНИЯ В НАБОРЕ ДАННЫХ С БАЗОЙ ДАННЫХ В набор данных можно вносить любые изменения – добавлять новые строки, изменять и удалять существующие строки. Например, добавить строку в таблицу Sessions набора данных можно следующим образом: // создание строки на основе схемы таблицы Sessions набора данных DataRow newRow = ds.Tables[“Sessions”].NewRow(); // получилась пустая строка, столбцы которой должны быть заполнены newRow[“NumGroup”]=”903”; newRow[“idSubject”]=5; // добавление созданной строки в таблицу ds.Tables[“Sessions”].Add(newRow); Однако все изменения происходят с отсоединенным набором данных, т.е. в оперативной памяти компьютера клиентского приложения, но не в базе дан- ных. Чтобы изменения были внесены в исходную базу данных, требуется син- хронизировать набор данных и базу с помощью функции класса-адаптера Up- date(). Однако такая синхронизации возможна только в случае, когда объект- адаптер имеет инициализированные свойства-команды SelectCommand, Insert- Command, UpdateCommand, DeleteCommand. По умолчанию при создании адаптера инициализируется только SelectCommand. Возможно задание осталь- ных команд «вручную». Для удобства и стандартной инициализации всех свойств-команд адапте- ра существует специальный класс OdbcCommandBuilder, который генерирует все команды и заполняет соответствующие свойства адаптера. После этого можно проводить синхронизацию изменений с помощью функции Update(). Приведем пример такого программного кода: // создание подключения к базе данных на основе строки соединения // с указанием источника данных ODBC OdbcConnection con = new OdbcConnection("DSN=proba"); // подключение к источнику данных con.Open(); // формирование адаптера для связи с базой данных OdbcDataAdapter adapt = new OdbcDataAdapter ("select * from Sessions", con); //создание команд для адаптера OdbcCommandBuilder cb = new OdbcCommandBuilder(adapt); 72 // создание набора данных для сохранения результата запроса DataSet ds = new DataSet(); // заполнение таблицы набора данных (второй параметр) // из базы данных через адаптер adapt.Fill(ds,”Sessions”); // любые действия с набором данных – изменение существующих строк, // добавление новых, удаление // синхронизация изменений с базой данных adapt.Update(); // закрывается соединение con.Close(); 2.7. ПОЛЬЗОВАТЕЛЬСКИЙ ИНТЕРФЕЙС НА ОСНОВЕ ТАБЛИЦ Для такого представления данных Windows.Forms имеет специальный элемент управления DataGridView. С помощью этого элемента управления можно добавлять новые записи, изменять значения в ячейках, удалять строки. Программный интерфейс этого класса достаточно сложный, но простые опции можно реализовать минимальными усилиями. Главное условие, чтобы этот элемент управления работал с данными – назначение источника данных для не- го (свойство DataSource). Кстати, источником данных может быть любая кол- лекция – список объектов класса, таблица из набора данных и пр. public Form1() { InitializeComponent(); OdbcConnection con = new OdbcConnection("DSN=proba"); con.Open(); OdbcDataAdapter adapt = new OdbcDataAdapter ("select * from Sessions", con); ds = new DataSet(); adapt.Fill(ds, "Sessions"); con.Close(); dataGridView1.DataSource = ds.Tables["Sessions"]; } 73 Рис. 42. Загрузка в DataGridView данных из назначенного источника данных. Однако, как видно из предшествующего рисунка, настройки внешнего вида элемента управления (заголовки столбцов) назначаются автоматически, исходя из схемы таблицы-источника данных. На следующем рисунке показан интерфейс, который подстроен под поль- зователя. Для этого требуется провести программную настройку элемента DataGridView – программное создание всех столбцов и привязка их к полям ис- точника данных. Привязка к полям осуществляется с помощью объекта класса BindingSource, который является своеобразным посредником между элементом управления и источником данных. Рис. 43. Вид после программной настройки элемента DataGridView. public partial class Form2 : Form { DataSet ds; BindingSource bindsrc = new BindingSource(); public Form2() { InitializeComponent(); OdbcConnection con = new OdbcConnection("DSN=proba"); con.Open(); OdbcDataAdapter adapt = new OdbcDataAdapter ("select * from Sessions", con); ds = new DataSet(); 74 adapt.Fill(ds, "Sessions"); con.Close(); // настройка BindingSource на источник данных – // таблицу набора данных bindsrc.DataSource = ds.Tables[0]; // отмена генерации столбцов DataGridView dataGridView1.AutoGenerateColumns = false; // установка привязки к источнику данных dataGridView1.DataSource = bindsrc; // последовательное создание столбцов элемента управления DataGridViewTextBoxColumn NumGroup = new DataGridViewTextBoxColumn(); // имя поля, которое является источником данных для столбца NumGroup.DataPropertyName = "NumGroup"; // заголовок столбца NumGroup.HeaderText = "Учебная группа"; // добавление столбца в коллекцию столбцов DataGridView dataGridView1.Columns.Add(NumGroup); DataGridViewComboBoxColumn colSem = new DataGridViewComboBoxColumn(); colSem.DataPropertyName = "NumSemestr"; colSem.HeaderText = "Семестр"; colSem.DataSource = new int[] { 1, 2 }; dataGridView1.Columns.Add(colSem); DataGridViewTextBoxColumn colTitle = new DataGridViewTextBoxColumn(); colTitle.DataPropertyName = "idSubject"; colTitle.HeaderText = "Дисциплина"; dataGridView1.Columns.Add(colTitle); DataGridViewTextBoxColumn colZE = new DataGridViewTextBoxColumn(); colZE.DataPropertyName = "Zach_Exam"; colZE.HeaderText = "Отчетность"; dataGridView1.Columns.Add(colZE); } 2.8. ПОЛЬЗОВАТЕЛЬСКИЙ ИНТЕРФЕЙС НА ОСНОВЕ ОДНОЙ ЗАПИСИ Чтобы просмотреть все записи, которые были помещены в результат за- проса к базе данных, можно использовать интерфейс последовательного покор- тежного просмотра. Такой интерфейс удобен, например, для приложения те- стирования, когда на форме показан только один из вопросов теста. 75 Для навигации по строкам источника данных можно самостоятельно со- здать панель инструментов, с помощью которой можно было бы передвигаться по записям, добавлять, изменять и удалять записи источника данных. Но среди доступных элементов управления существует элемент, который уже реализует стандартный вид такой панели. Этот элемент управления называется Binding- Navigator и для его корректной работы достаточно настроить только тот же ис- точник данных, что для элементов управления, предоставляющих пользователю данные. public partial class Form3 : Form { DataSet ds; // посредник между источником данных и элементами управления BindingSource bindsrc = new BindingSource(); public Form3() { InitializeComponent(); OdbcConnection con = new OdbcConnection("DSN=proba"); con.Open(); OdbcDataAdapter adapt = new OdbcDataAdapter ("select * from Sessions", con); ds = new DataSet(); adapt.Fill(ds, "Sessions"); con.Close(); // настройка источника данных для посредника bindsrc.DataSource = ds.Tables[0]; // настройка источников данных для элементов управления // первый параметр – свойство элемента управления, // отображающего данные // второй параметр – источник данных // третий параметр – имя свойства из источника данных, // данные которого отображаются в элементе управления textBox1.DataBindings.Add("Text", bindsrc, "NumGroup"); textBox2.DataBindings.Add("Text", bindsrc, "TitleSubject"); textBox3.DataBindings.Add("Text", bindsrc, "Zach_Ezam"); // настройка источника данных для панели навигации bindingNavigator1.BindingSource = bindsrc; } } 76 Рис. 44. Вид формы с использованием элемента BindingNavigator. Отметим, что любой элемент управления на такой форме также должен быть привязан к своему источнику данных, который задается с помощью свой- ства (коллекции) DataBindings. Метод добавления нового источника для эле- мента управления задается с помощью трех параметров: 1 – свойство элемента управление, которое принимает значение из источника, 2 – объект источника данных или посредника BindingSource, 3 – свойство из источника данных, ко- торое отображается в элементе управления. 2.9. ГЕНЕРАЦИЯ ОТЧЕТОВ И ПЕЧАТНЫХ ФОРМ Помимо форм для представления и редактирования информации из базы данных пользователю часто требуется создавать печатные формы, представля- ющие результаты выполнения сложных запросов. Такие печатные формы при- нято называть отчетами. Для формирования отчетов в среде Visual Studio суще- ствует специальный элемент управления, который называется ReportView. Су- ществуют и более сложные дополнительные утилиты (например, не так давно была популярна условно бесплатная утилита CrystalReports). Для добавления в проект отчета требуется установить на форму элемент управления ReportView. Далее с помощью конструктора можно сгенерировать новый вид отчета, который будет сохранен в файле с расширением rdlc. Панель BindingNavigator 77 Рис. 45. Вызов конструктора отчета. Конструктор позволит настроить источники данных, сгенерировать рас- положение элементов на отчете, задать стилевые и другие характеристики. Рис. 46. Вид сгенерированного шаблона отчета. В сгенерированный отчет можно вносить изменения – добавлять поля, элементы управления, менять стилевые назначения и пр. Для использования и показа отчета в элементе управления ReportViewer достаточно сделать несколько простых настроек в программном коде: // указание источника данных для отчета – таблица студентов, // загруженная в набор данных DataSet ReportDataSource datasource = new ReportDataSource("DataSet1", ds.Tables["Students"]); // настройка на локальный отчет reportViewer1.ProcessingMode = ProcessingMode.Local; // очистка старого источника данных отчета из элемента ReportViewer 78 reportViewer1.LocalReport.DataSources.Clear(); // настройка пути к файлу с отчетом (.rdlc) reportViewer1.LocalReport.ReportPath = "../../Report1.rdlc"; // добавление источника данных (таблицы из набора данных) к отчету reportViewer1.LocalReport.DataSources.Add(datasource); // обновление содержимого элемента ReportViewer reportViewer1.RefreshReport(); Результат внедрения в элемент управления отчета может выглядеть сле- дующим образом: Рис. 47. Вид сгенерированного отчета. 2.10. ГЕНЕРАЦИЯ ОТЧЕТОВ В ФОРМАТЕ XML Еще одним способом генерации печатных форм является генерация отче- тов в HTML-формате на основе формирования XML-файлов и последующего применения к ним XSLT-преобразований. Этот подход генерации отчетов явля- ется более универсальным, так как формат XML и его технологии являются межплатформенной и поддерживаются большим количеством языков програм- мирования и средами проектирования в различных операционных системах. Например, XSLT-преобразование делается на основе следующего файла. В нем создается список зачетов и экзаменов, которые сдают студенты различ- ных групп. С помощью выражений XPath в тегах for-each и value-of задаются источники данных из xml-документа для конкретных элементов html-файла, полученного в результате преобразования: |