Собственная база данных на основе XML файла

К началу раздела

Раздел 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.). Наше решение будет предназначено для подготовки информации к выставке собак и показе информации на самой выставке (естественно, что это упрощенное решение).

xml0020.gif

Рис.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". Если мы хотим иметь файл нашей базы данных более читаемым, поступим следующим образом:

  • 1. В обработчике события формы вернемся к кодировке "Default":

    using (StreamReader streamreader = new StreamReader(sCurDir + @"\sales1.xml",
                               System.Text.Encoding.Default))
    {
     MyDataSet = new DataSet();
     MyDataSet.ReadXml(streamreader, XmlReadMode.Auto);
     MyDatatable = MyDataSet.Tables[0];
    }
    
  • 2. При сохранении DataSet вместо строк

     MyDataSet.WriteXml(sCurDir + @"\sales1.xml",XmlWriteMode.WriteSchema);
     MyDataSet = new DataSet();
     MyDataSet.ReadXml(sCurDir + @"\sales1.xml",XmlReadMode.Auto);
     MyDatatable = MyDataSet.Tables[0];
    

    запишем код:

     xmldoc = new XmlDocument();
     xmldoc.InnerXml = MyDataSet.GetXml();            
     XmlDeclaration xmldeclaration =
        xmldoc.CreateXmlDeclaration("1.0", "windows-1251", "yes");
     xmldoc.InsertBefore(xmldeclaration, xmldoc.DocumentElement);
     xmldoc.Save(sCurDir + @"\sales1.xml");            
     MyDataSet = new DataSet();
     MyDataSet.ReadXml(sCurDir + @"\sales1.xml",XmlReadMode.Auto);
     MyDatatable = MyDataSet.Tables[0];
    

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 решениям).

xml0021.gif

Рис.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 сайты).

В начало страницы

К началу раздела

В начало книги

На главную страницу сайта


Сайт управляется системой uCoz