Раздел 10. Глава 9. Собственная база данных на основе XML файлаВведениеКогда создаются программы, использующее одну или несколько таблиц баз данных и (или) таблицы базы данных требуются только одному приложению (ограниченному числу приложений), то достаточно неудобно при сопровождении приложения отслеживать установки клиентского программного обеспечения для работы с базами данных, выполнять соответствующие настройки, решать вопросы безопасности и т.д. и т.п. В Borland C++ Builder (Borland Delphi) для таких ситуаций можно использовать компонент TClientDataSet, позволяющий создать из таблиц БД файл, который далее использовать в приложении полностью аналогично таблице базы данных. Собственно компонент TCLientDataSet позволял также, после загрузки файла из реальной таблицы БД, забыть на время о сервере доступа к данным и работать только с этим файлом (выполнять любое редактирование), а на этапе завершения приложения или на этапе необходимости публикации данных - перенести нужные данные в реальную таблицу БД. В Visual Studio Net контрола, или элемента подобного компоненту TCLientDataSet, нет. Но все, что позволял компонент TCLientDataSet с таким же успехом можно выполнить, используя XML файлы. Мы уже пользовались возможностью загрузки данных из таблиц БД в DataSet и обратного сохранения данных. В данной главе мы будем использовать загрузку данных из XML файла в DataSet, отметив, что после загрузки данных, доступ к данным ничем не отличается от работы с данными при их загрузке из таблиц БД. Учитывая возможность работы с двоичными данными, описанную в предыдущей главе, мы можем непосредственно приступить к описанию создания собственной базы данных на основе XML файла и работы с ней. Параграф 1. Создание файла базы данныхXML файл базы данных может быть создан на основе реальной таблицы базы данных. Для этого необходимо загрузить DataSet данными реальной таблицы (как это сделать - см. "Основы работа с сервером SQL в Visual Studio .NET" и "Работа с сервером SQL в Visual Studio 2005 (ASP.NET 2)") и сохранить DataSET как XML файл. Второй вариант, создать заготовку файла в текстовом редакторе (см. предыдущую главу "Параграф 2. Сохраняем рисунок в XML файл"). Пойдем по второму пути и, как и в предыдущей главе, сначала создадим простое решение приложения, имеющее три кнопки (три контрола Button на форме, пока без обработчиков событий нажатия кнопок), контрол PictureBox, три контрола TextBox, контрол OpenFileDialog и несколько контролов Label (Рис.1.). Наше решение будет предназначено для подготовки информации к выставке собак и показе информации на самой выставке (естественно, что это упрощенное решение).
Рис.1 Решение приложения Выполним решение в режиме Release. В директорию, где будет сформирован exe файл приложения (у меня C:\Samples_C#_2008\WindowsFormsApplication2\bin\Release\), поместим XML файл (хотя поместить файл можно и куда угодно на жестком диске). Пусть это будет файл с именем sales1.xml, а его содержимое - данные о собачках, подготовленных к продаже неким обществом, этим занимающимся: <?xml version="1.0" encoding="windows-1251"?> <NewDataSet> <dog> <id></id> <name></name> <pic></pic> <picext></picext> <price></price> </dog> </NewDataSet> Первая строка XML документа называется объявлением XML (declaration) это необязательная строка, указывающая версию стандарта XML. Здесь может быть указана кодировка символов (encoding="windows-1251") и внешние зависимости. Заголовок может содержать атрибут "самодостаточности". Указание кодировки символов, к сожалению, является обязательным. DataSet при загрузке в него XML документов учитывает информацию мегатэгов, но нигде ее не сохраняет и, поэтому, добавление метатэга с параметром "encoding" придется выполнять ручками при сохранении документа или использовать сохранение документа с записью его схемы (оба способа показаны ниже). Обратим внимание на тэг "NewDataSet". Поскольку мы работаем с DataSet, то при сохранении содержимого DataSet в XML файл, данный тэг добавляется автоматически. Кроме этого файл имеет тэги "dog" - один на каждый экземпляр, "id" - идентифицирующий номер записи, "name" - имя собачки, "pic" - рисунок, "picext" - формат рисунка и "price" - цена. Параграф 2. Загрузка XML файла, ввод и сохранение данныхВ начале определим все глобально используемые в пределах приложения переменные: public partial class Form1 : Form { //Директория выполнения приложения private string sCurDir = string.Empty; //Класс XML документа private XmlDocument xmldoc = null; //Классы для работв с XML документом как с объектом базы данных DataTable MyDatatable = null; DataSet MyDataSet = null; В обработчике события Load формы будем выполнить начальную загрузку XML файла: private void Form1_Load(object sender, EventArgs e) { sCurDir = Directory.GetCurrentDirectory(); label1.Text = ""; label2.Text = ""; using (StreamReader streamreader = new StreamReader(sCurDir + @"\sales1.xml", System.Text.Encoding.UTF8)) { MyDataSet = new DataSet(); MyDataSet.ReadXml(streamreader, XmlReadMode.Auto); MyDatatable = MyDataSet.Tables[0]; } } Обратим внимание на параметр метода ReadXml "XmlReadMode.Auto" - он позволит DataSet правильно создать схему с учетом значений мегатэгов, а также на значение "Encoding.UTF8". Причина использования именно этой кодировки - DataSet по умолчанию будет сохранять данные в кодировке UTF8. Итак, на данном этапе, пустой XML файл загружен в DataSet с именем MyDataSet. Выполним выбор рисунка в PictureBox для его последующего сохранения в файл и ввод имени и цены в контролы TextBox, для чего используем следующие обработчики событий: private void button2_Click(object sender, EventArgs e) { openFileDialog1.InitialDirectory = sCurDir; openFileDialog1.Filter = "gif files(*.gif)|*.gif|bmp files(*.bmp)|*.bmp"; if (openFileDialog1.ShowDialog() == DialogResult.OK) { pictureBox1.Image = Image.FromFile(openFileDialog1.FileName); //Расширение сохраняем в TextBox4 textBox4.Text = Path.GetExtension(openFileDialog1.FileName); } } //Цена в TtextBox3 может быть только цифра private void textBox3_KeyPress(object sender, KeyPressEventArgs e) { if (!Char.IsDigit(e.KeyChar)) { e.Handled = true; } } Осталось сохранить данные. private void button3_Click(object sender, EventArgs e) { //Сохранять не будем - если что-то не ввели if (pictureBox1.Image == null) return; if (textBox4.Text == "") return; if (textBox2.Text == "") return; if (textBox3.Text == "") return; //Строки для метода Select в XML документе DataRow[] datarows = null; //Ищем максимальное ID в DataSet (в Datatable) string s = string.Empty; try { datarows = MyDatatable.Select("id=max(id)"); s = datarows[0]["id"].ToString(); } catch (Exception) { } if (s == "" || s == string.Empty) { s = "0"; } //Для формирования строки рисунка создаем StringBuilder StringBuilder MyStringBuilder = new StringBuilder(); int i = int.Parse(s)+1; //Создаем новую строку для MyDataSet DataRow datarow = MyDataSet.Tables[0].NewRow(); //Присваиваем значения столбцам строки datarow[0] = Convert.ToString(i); datarow[1] = textBox2.Text.Trim(); //Формируем строковое представление рисунка (см. предыдущую главу) using (MemoryStream memorystream = new MemoryStream()) { pictureBox1.Image.Save(memorystream, ImageFormat.Gif); byte[] b = new byte[memorystream.Length]; //memorystream.Read(b, 0, (int)memorystream.Length); b = memorystream.GetBuffer(); s = string.Empty; foreach (Byte zb in b) { int a = (int)zb; MyStringBuilder.Append(a.ToString("X2")); } datarow[2]=Convert.ToString(MyStringBuilder); } datarow[3] = textBox4.Text.Trim(); datarow[4] = textBox3.Text.Trim(); MyDataSet.Tables[0].Rows.Add(datarow); //Удаляем строку с пустыми значениями, которые при первоначальной //загрузке были использованы для формирования схемы if (i == 1) { MyDataSet.Tables[0].DefaultView.AllowDelete = true; MyDataSet.Tables[0].DefaultView.Delete(0); } pictureBox1.Image = null; textBox4.Text=""; textBox3.Text=""; textBox2.Text=""; //Сохраняем данные MyDataSet.WriteXml(sCurDir + @"\sales1.xml",XmlWriteMode.WriteSchema); MyDataSet = new DataSet(); //Вновь загружаем сохраненные данные MyDataSet.ReadXml(sCurDir + @"\sales1.xml",XmlReadMode.Auto); MyDatatable = MyDataSet.Tables[0]; } Файл XML будет иметь вид: <?xml version="1.0" standalone="yes"?> <NewDataSet> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="dog"> <xs:complexType> <xs:sequence> <xs:element name="id" type="xs:string" minOccurs="0" /> <xs:element name="name" type="xs:string" minOccurs="0" /> <xs:element name="pic" type="xs:string" minOccurs="0" /> <xs:element name="picext" type="xs:string" minOccurs="0" /> <xs:element name="price" type="xs:string" minOccurs="0" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element ref="dog" /> </xs:choice> </xs:complexType> </xs:element> </xs:schema> <dog> <id>1</id> <name>РЁР°С_РїРчР№ в"- 1</name> <pic>474946383961C5006500F70000000000800000008000808000000080800080008..............</pic> <picext>.gif</picext> <price>2000</price> </dog> </NewDataSet> Обратим внимание, что кодировка файла, как и предполагалось, "UTF8". Если мы хотим иметь файл нашей базы данных более читаемым, поступим следующим образом:
XML файл будет выглядеть следующим образом: <?xml version="1.0" encoding="windows-1251" standalone="yes"?> <NewDataSet> <dog> <id>1</id> <name>Шарпей № 1</name> <pic>474946383961C5006500F700000000008000000080008080000000........</pic> <picext>.gif</picext> <price>2000</price> </dog> <dog> <id>2</id> <name>Шарпей № 2</name> <pic>474946383961C5006500F700000000008000000080008080000000........</pic> <picext>.gif</picext> <price>5000</price> </dog> </NewDataSet> Параграф 3. Отображение данныхДанные загружаются при старте приложения и при сохранении новой записи в DataSet. С этого момента мы можем работать с ними как с данными, полученными с реальной таблице базы данных (см. "Основы работа с сервером SQL в Visual Studio .NET" и "Работа с сервером SQL в Visual Studio 2005 (ASP.NET 2)") В качестве примера приведем отображение данных по запросу. В качестве запроса будем использовать информацию о ID записи, которую будем вводить в TextBox1: //ID может быть только цифра private void textBox1_KeyPress(object sender, KeyPressEventArgs e) { if (!Char.IsDigit(e.KeyChar)) { e.Handled = true; } } private void button3_Click(object sender, EventArgs e) { if (textBox1.Text.Trim() != "") { textBox2.Text = ""; textBox3.Text = ""; textBox4.Text = ""; pictureBox1.Image = null; getDog(textBox1.Text.Trim()); } } private void getDog(string num) { DataRow[] datarows = null; try { datarows = MyDatatable.Select("id=" + num); } catch (Exception ex) { return; } if (datarows.Length > 0) { foreach (DataRow datarow in datarows) { label1.Text = datarow["name"].ToString(); string s3 = datarow["pic"].ToString(); label2.Text = "Цена: "+datarow["price"].ToString(); using (MemoryStream memorystream = new MemoryStream()) { for (int i = 0; i < s3.Length / 2; i++) { if (i * 2 + 1 < s3.Length) { string s02 = Convert.ToString(s3[i * 2]); string s03 = Convert.ToString(s3[i * 2 + 1]); string s04 = s02 + s03; int a = int.Parse(s04, System.Globalization.NumberStyles.HexNumber); memorystream.WriteByte(Convert.ToByte(a)); } } pictureBox1.Image = Image.FromStream(memorystream); } } } } } Обратим внимание на формат рисунка (документа). Мы уже отмечали, что точно таким образом можно сохранять и восстанавливать любые данные (Web страницы, Word или Excel документы и т.д. и т.п...). При отображении, сохранении и преобразованиях эта информация нами может быть использована. Что касается рисунков, то подробно о преобразованиях форматов можно посмотреть в главе О возможностях преобразований графических файлов при их отображении на сайте (все что касается преобразований равным образом относится к Web и Windows решениям).
Рис.2 Выполнение приложения И последнее замечание. Описанный способ работы с XML данным с использованием DataSet может быть удобной заменой ini-фалов приложений. По крайней мере, доступ к строке параметра по имени в DataSet значительно проще, чем его позиционный или лексический поиск в ini-файле. Молчанов Владислав 16.12.2007г. Еcли Вы пришли с поискового сервера - посетите мою главную страничкуНа главной странице Вы найдете программы комплекса Veles - программы для автолюбителей,
программу NumberPhoto, созданную для работы с фото, сделанными цифровым фотоаппаратом,
программу Локальный Web сайт - предназначенную для просмотра и прослушивания
файлов большинства графических и звуковых форматов в Web Browser,
программу Bricks - игрушку для детей и взрослых, программу записную книжку,
программу TellMe - говорящий Русско-Английский разговорник - программу для тех, кто собирается
погостить за бугром или повысить свои знания в английском, теоретический материал
по программированию в среде Borland C++ builder, C# (Windows приложения и ASP.Net Web сайты).
|