Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

VPS в России, Европе и США

Бесплатная поддержка и администрирование

Оплата российскими и международными картами

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

2006 г.

Руководство по работе с БД Firebird с использованием библиотеки ADO .Net 2.0

Меркулов Андрей Александрович, http://www.ibprovider.com/

Предисловие

В данном обзоре будет описан один из способов работы с базой данных Firebird в среде .Net при помощи управляемого Ole Db провайдера. Несомненным преимуществом сервера баз данных Firebird является его бесплатность в сравнении с существующими, не очень дешевыми аналогами (прежде всего MS SQL Server и ORACLE).

Firebird можно использовать для систем практически любого уровня, начиная от однопользовательских настольных приложений со встроенной базой данных (Embed Database), до клиент-серверных приложений уровня корпорации.

Средства и технологии, используемые в статье:

  • ADO .Net 2.0
  • Data Protection API (DAPI)
  • Visual Studio 2005 Professional
  • Firebird SQL Server 2.0
  • IBProvider v.3 и IBProvider v.2

Что такое OLE DB Provider?

Для доступа к базам данных в ADO (а теперь эта возможность есть и в ADO.Net) используются OLE DB провайдеры. Ole Db Provider представляет собой драйвер для доступа к базе данных при помощи OLE DB интерфейсов. Для взаимодействия с OLE DB провайдером в .Net реализовано пространство имен System.Data.OleDb.

При работе с Firebird я использую IBProvider (www.ibprovider.com) и в своем повествовании буду опираться, прежде всего, на его функциональность. Разработчики IBProvider поддерживают три коммерческих версии драйвера. Так же есть бесплатная. Для написания примеров применялся IBProvider третьей версии и только для примера управляющих ODBC последовательностей использовался IBProvider v2.

Список основных различий в версиях IBProvider:

Возможности IBProvider Free IBProvider v1/IBProvider v2 IBProvider v3
Поддержка всей линейки серверов Interbase, Yaffil, Firebird, включая FB2 да да да
Возможность работы с ADO и соответственно с Microsoft Office, VBA, VBScript, Java Script и др. да да да
Отсутствие ограничений на размер получаемых данных да да да
Поддержка ADO.Net нет да да
Возможность работы в качестве MS SQL Linked Server нет да да
Работа с метаданными нет да да
Поддержка обновляемых множеств нет нет/да пока нет
Поддержка распределенных транзакций нет да да
Вложенные транзакции нет нет да
Поддержка управляющих последовательностей ODBC для SQL запросов (используется в Crystal Reports, MS SQL Server, различные OLAP и др. инструменты) нет да пока нет
Поддержка DDL запросов да, но без поддержки SQL парсером провайдера да да
Тип закладок 4 байта 4 байта/8 байт 8 байт
Поддержка Client Cursor Engine нет да да
Уведомления о завершении транзакции нет нет да
Предоставление расширенной информации о сервере (версия, тип сервера, размер страницы). нет да да
Возможность задавать используемую клиентскую библиотеку (gds32.dll или fbclient.dll) нет нет да
Поддержка изменений в DML (Data Model Language) для Firebird 2 нет нет да
Новые свойства и алгоритмы Firebird 2 (возможность узнать дату создания базы и т.п.) нет нет да

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

Методы подключения к базе данных

Параметры строки подключения

Для использования Ole Db провайдера необходимо подключить соответствующее пространство имен к нашему проекту:

using System.Data.OleDb;

Управление подключением к Ole Db источникам данных осуществляется с помощью класса OleDbConnection. Самый простой способ подключения к базе данных – прямое указание строки подключения в конструкторе этого класса:

OleDbConnection con = new OleDbConnection(connectionString);            
con.Open();
con.Close();

Для формирования строки подключения в Net 2.0 появился класс OleDbConnectionStringBuilder:

OleDbConnectionStringBuilder cb = new OleDbConnectionStringBuilder();
cb.Provider = "LCPI.IBProvider";            
cb.Add("Location",@"localhost:d:\Program Files\Firebird\examples\EMPLOYEE.FDB");
cb.Add("User ID", "sysdba");
cb.Add("Password", "masterkey");
cb.Add("ctype", "win1251");
Console.WriteLine(cb.ToString())

Существует определенный набор свойств инициализации IBProvider-a, который необходимо установить перед выполнением соединения с БД:

Обязательные свойства инициализации (параметры подключения) IBProvider:

Свойство Описание
Location Путь к базе данных на сервере.
Provider Имя Ole Db провайдера
User ID Имя пользователя базы данных
Password Пароль пользователя
Ctype Кодировка определяет, символы какого национального алфавита будут использоваться. Для русского и английского алфавита можно использовать кодировку WIN1251

Некоторые необязательные свойства инициализации IBProvider:

Свойство Описание
Data Source Данное свойство используется для задания user friendly имени для базы данных, например "Employee DB". Если свойство Loсation не определено, то предполагается, что в Data Source указано расположение базы данных.
db_client_type Тип клиента сервера базы данных. Есть только в IBProvider v3.
db_client_library DLL с клиентом сервера
auto_commit Режим автоматического подтверждения транзакций. Для его включения в строке подключения необходимо указать “auto commit =true”.
role Роль пользователя

Более подробно о свойствах инициализации IBProvider-а можно прочитать здесь

СОВЕТ.  Всегда включайте в параметр Location имя сервера базы данных. Это позволит обеспечить совместимость со всем версиями Firebird
Способы хранения строк подключения
В реальных приложениях никто не прописывает строки подключения к базе данных в коде. Гораздо эффективнее использовать для этой цели либо настройки приложения (технология Settings), либо отдельный файл подключения.

Для хранения параметров подключения в Windows существует специальный тип файлов Microsoft Data Link – это файл с расширением udl. С этим расширением ассоциирован универсальный редактор подключений. IBProvider поддерживает свои собственные табы, которые предоставляют удобный интерфейс для формирования параметров соединения. Для того чтобы использовать udl файл в своем приложении, выполните следующие шаги:

  • Создайте пустой файл с расширением .udl
  • Откройте файл (Enter), появится связанный с данным расширением диалог для настройки подключения:
  • В списке OleDb провайдеров выберете IBProvider v3:

datalink1.gif

  • Задайте параметры подключения по аналогии с рисунком и нажмите кнопку «Проверить подключение»:

datalink2.gif

  • На вкладке дополнительно вы можете задать расширенные свойства подключения:

datalink3.gif

  • Нажмите «Ок» для записи информации о подключении в файл

Теперь для того, чтобы использовать подключение, описанное udl файлом, достаточно явно или через OleDbConnectionStringBuilder задать свойство File Name

OleDbConnectionStringBuilder cb = new OleDbConnectionStringBuilder();
cb.FileName = AppDomain.CurrentDomain.BaseDirectory + @"\employee.udl";
OleDbConnection con = new OleDbConnection(cb.ToString());
con.Open();

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

В свойствах проекта выберите вкладку Settings и создайте новое свойство с именем ConnectionString и типом (Connection string):

datalinkIDE_CreateSetting.gif

При редактировании свойства запустится встроенный в VS 2005 редактор строки подключения:

datalinkIDE.gif



ПРИМЕЧАНИЕ.   Если нажать на кнопку “Data Links”, то появится уже знакомый нам диалог конфигурации Microsoft Data Link

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

Properties.Settings s = new Properties.Settings();
//чтение свойства с именем ConnectionString
Console.WriteLine(s.ConnectionString); 

Для облегчения написания примеров был создан класс ConnectionProvider, который инкапсулирует в себе все, описанные методы подключения.

Шифрование строки подключения. Data Protection API
Один из вариантов защитить строку подключения в своем конфигурационном файле - это воспользоваться Data Protection API (DAPI). Начиная с Windows 2000, DAPI является частью операционной системы.

Допустим, нам необходимо зашифровать данные, хранящиеся в секции connectionStrings. Для этого мы воспользуемся классом DataProtectionConfigurationProvider:

Подключим к нашему проекту сборку System.Configuration.dll и используем следующий код:

public void DataProtectionAPITest()
{
    try
    {          
    //открываем секцию connectionStrings из App.config               
    Configuration config = ConfigurationManager.OpenExeConfiguration(
    System.Reflection.Assembly.GetExecutingAssembly().Location);

    ConnectionStringsSection section =
    config.GetSection("connectionStrings") as ConnectionStringsSection;

    if (!section.SectionInformation.IsProtected)
       {
         // выполняем шифрование секции
         section.SectionInformation.ProtectSection(
         "DataProtectionConfigurationProvider");
         // Сохраняем конфигурацию
         config.Save();
       }

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

    // получаем строку подключения из зашифрованной секции
    Console.WriteLine(Properties.Settings.Default.ConnectionString);
}  
ПРЕДУПРЕЖДЕНИЕ.   Данные, хранящиеся в секции, могут быть расшифрованы только на том компьютере, на котором были зашифрованы. Таким образом, процедуру шифрования данных необходимо вызывать на компьютере конечного пользователя.

Данный пример как раз подходит для этих целей. При установке приложения мы помещаем в папку с программой ещё не зашифрованный App.Config. При первом запуске приложения данная процедура его зашифрует и, в последствии, программа будет работать уже с защищенной секцией. Можно так же вызывать процедуру шифрования во время установки приложения.

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

Команды

Команды предназначены для передачи запросов базе данных. Для Ole Db провайдеров команда реализуется классом OleDbCommand. Команда всегда выполняется для заданного открытого подключения к базе данных в контексте транзакции.

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

  • Создать подключение к БД и открыть его
  • Создать активную транзакцию из текущего подключения – метод OleDbConnection.BeginTransaction()
  • Создать объект OleDbCommand, либо используя один из вариантов перегруженного конструктора, либо метод OleDbConnection.CreateCommand()
  • Установить свойство команды Transaction, если оно не было задано в конструкторе
  • Задать текст команды CommandText
  • Для обращения к базе данных у команды есть три метода ExecuteScalar, ExecuteReader и ExecuteNonQuery.
  • Завершить транзакцию OleDbTransaction.Commit() или откатить OleDbTransaction.Rollback() и закрыть подключение.
ExecuteScalar

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

public void ExecuteScalarTest()
{
   OleDbConnection con = ConnectionProvider.CreateConnection();
   con.Open();
   OleDbTransaction trans = con.BeginTransaction();

   OleDbCommand cmd = new OleDbCommand("select count(*) from employee", con, trans);   
   Console.WriteLine("Record count:" + cmd.ExecuteScalar().ToString());
  
   trans.Commit();
   con.Close();  
}
ExecuteReader
Данный метод возвращает объект OleDbDataReader, который по своему назначению очень близок объекту Recordset из классического ADO. Он использует однонаправленное ForwardOnly чтение данных, реализуя подсоединенную модель доступа. Таким образом, при его использовании необходимо наличие открытого подключения к базе.

Навигация по строкам результирующего множества осуществляется при помощи метода Read(), который возвращает true в случае, если ещё остались строки и false в противном случае. При вызове метода команды ExecuteReader(), созданный им объект OleDbDataReader не спозиционирован на первой строке результирующего множества и для её прочтения необходимо сначала вызвать метод Read().

Наиболее удобным способом чтения данных из результирующего множества является использование метода Read() совместно с конструкцией while:

public void ExecuteReaderTest()
{
   OleDbConnection con = ConnectionProvider.CreateConnection();
   con.Open();
            
   //Испольуем метод CreateCommand для создания команды
   OleDbCommand cmd = con.CreateCommand();
   cmd.Transaction = con.BeginTransaction();
   cmd.CommandText = "select * from employee";            
   OleDbDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

   //чтение данных
   while (rdr.Read())
   { 
      string tmp ="";
      for(int i=0; i<rdr.FieldCount -1;i++) 
      {
         if (tmp != "") tmp += "\t";
         tmp += rdr[i].ToString();
      }

      Console.WriteLine(tmp);  
   }

   rdr.Close();
   //после вызова OleDbDataReader.Close() подключение к БД будет закрыто     
   Assert.AreEqual(ConnectionState.Closed,con.State);  
}
ПРИМЕЧАНИЕ.   Обратите внимание, что после вызова метода OleDbDataReader.Close() подключение к базе данных будет закрыто. Это произошло потому, что я применил перегруженный метод ExecuteReader() с заданным параметром CommandBehavior.CloseConnection. По умолчанию после выполнения метода OleDbDataReader.Close() подключение к базе данных остается открытым
ExecuteNonQuery
Метод применяется для выполнения запросов, которые возвращают количество обработанных записей, таких как insert, update, delete, а так же для выполнения хранимых процедур, результат которых помещается в OUT параметры команды:
public void ExecuteNonQueryTest()
{
    OleDbConnection con = ConnectionProvider.CreateConnection();        
    con.Open();        
    OleDbTransaction trans = con.BeginTransaction();        

    //INSERT         
    OleDbCommand cmd = new OleDbCommand(        
    "insert into country (country,currency) values(:country,:currency) ", 
    con, trans);
            
    cmd.Parameters.AddWithValue("country", "Russia");
    cmd.Parameters.AddWithValue("currency", "Kopec");
           
    // количество обработанных строк
    Assert.AreEqual(1, cmd.ExecuteNonQuery());
 
    //UPDATE
    cmd = new OleDbCommand(
    "update country set currency=:currency where country =:country", con, trans);
            
    cmd.Parameters.AddWithValue("currency", "Rouble");
    cmd.Parameters.AddWithValue("country", "Russia");

    // количество обработанных строк
    Assert.AreEqual(1, cmd.ExecuteNonQuery());
       
    //DELETE
    cmd = new OleDbCommand(
    "delete from country where country =:country", con, trans);
            
    cmd.Parameters.AddWithValue("country", "Russia");

    // количество обработанных строк
    Assert.AreEqual(1, cmd.ExecuteNonQuery());

    trans.Commit();
    con.Close();  
}
Параметры команд

В большинстве случаев при выполнении команды требуется задать её параметры. Параметры добавляются в коллекцию Parameters. Они могут быть именованные и позиционные. Пример команды с позиционными параметрами:

insert into country (country,currency) values(?,?)

С именованными:

insert into country (country,currency) values(:country,:currency)

IBProvider сам умеет формировать список параметров, производя анализ SQL выражения. Но, к сожалению, в ADO .Net необходимо вручную добавлять эти параметры, т.к. команда не запрашивает их описание у Ole Db провайдера. Если вспомнить ADO, то в нем список параметров прекрасно формировался без необходимости вмешиваться в этот участок кода.

Для того, чтобы добавить параметр, нужно воспользоваться:

  • для добавления именованного параметра и значения - методом AddWithValue()
  • для добавления как именованных, так и неименованных параметров - перегруженным методом Add()

Если не указан тип параметра, он будет добавлен с Ole Db типом VarWChar, что соответствует .Net типу string, что кажется разумным. Об этом не стоит беспокоиться, т.к. IBProvider корректно обрабатывает приведение любых типов Firebird.

Нельзя не сказать о существующих ограничениях при использовании именованных параметров совместно с OleDbCommand. В MSDN написано, что именованные параметры поддерживаются только для поставщиков данных MSSQL и Oracle, а для поставщиков данных Ole Db и ODBC поддерживаются только позиционные параметры. Использовать именованные параметры все же можно, но их добавление в коллекцию Parameters необходимо осуществлять в том же порядке, в каком они следуют в запросе. К примеру, если текст команды:

update country set currency=:currency where country =:country 

то сначала необходимо добавить параметр currency, а потом country:

cmd.Parameters.AddWithValue("currency", "Rouble");
cmd.Parameters.AddWithValue("country", "Russia");

Задавать значения параметров можно уже в произвольном порядке:

cmd.Parameters["country"].Value = "Latvia";
cmd.Parameters["currency"].Value = "Lat";
Вызов хранимых процедур
Существуют два способа обработки результатов хранимых процедур:
  • хранимая процедура возвращает результирующее множество
  • результат выполнения хранимой процедуры помещается в OUT параметры команды

Для первого способа используется обычная SQL-инструкция:

 select * from stored_procedure_name(…)

Результат её выполнения обрабатывается при помощи объекта OleDbDataReader:

public void StoredProcedureResultSetTest()
{
   OleDbConnection con = ConnectionProvider.CreateConnection();
   con.Open();
   OleDbTransaction trans = con.BeginTransaction();

   //select stored procedure in params 
   OleDbCommand cmd_in_params = 
new OleDbCommand("select cust_no from CUSTOMER", con, trans);
      
  //select mail label through the stored procedure
  OleDbCommand cmd_stored_proc = 
new OleDbCommand("select * from mail_label(:cust_no)", con, trans);
  
  //add one IN parameter                     
  cmd_stored_proc.Parameters.Add("cust_no", OleDbType.Integer);

  //execure reader
  using (OleDbDataReader rdr = cmd_in_params.ExecuteReader(CommandBehavior.CloseConnection))
  {
     //for each customer No  
     while (rdr.Read())
     {                    
        cmd_stored_proc.Parameters["cust_no"].Value = rdr["cust_no"];
        using (OleDbDataReader rdr_out = cmd_stored_proc.ExecuteReader())
        {
           Console.WriteLine("Customer №" + rdr["cust_no"]);                   
           while (rdr_out.Read())
                  for (int i = 0; i < rdr_out.FieldCount; i++)
                    Console.WriteLine(rdr_out.GetName(i) + "=" + rdr_out[i]);

           Console.WriteLine();
        }
                    
     }

  }
}

Второй способ – вызов хранимой процедуры через инструкцию:

 execute procedure stored_procedure_name

Результат выполнения помещается в OUT параметры команды, которые предварительно необходимо создать:

public void StoredProcedureOUTParamsTest()
{
   OleDbConnection con = ConnectionProvider.CreateConnection();
   con.Open();
   OleDbTransaction trans = con.BeginTransaction();

   //select in params 
   OleDbCommand cmd_in_params =
     new OleDbCommand("select cust_no from CUSTOMER", con, trans);

   //STORED PROCEDURE
   OleDbCommand cmd_stored_proc = 
     new OleDbCommand("execute procedure mail_label(:cust_no)", con, trans);

   //IN parameter
   cmd_stored_proc.Parameters.Add("cust_no", OleDbType.BSTR);
   //OUT parameters
   cmd_stored_proc.Parameters.Add("line1", OleDbType.BSTR)
                   .Direction = ParameterDirection.Output;         
   cmd_stored_proc.Parameters.Add("line2", OleDbType.BSTR)
                   .Direction = ParameterDirection.Output;
   cmd_stored_proc.Parameters.Add("line3", OleDbType.BSTR)
                   .Direction = ParameterDirection.Output;
   cmd_stored_proc.Parameters.Add("line4", OleDbType.BSTR)
                   .Direction = ParameterDirection.Output;
   cmd_stored_proc.Parameters.Add("line5", OleDbType.BSTR)
                   .Direction = ParameterDirection.Output;
   cmd_stored_proc.Parameters.Add("line6", OleDbType.BSTR)
                    .Direction = ParameterDirection.Output;

   //execure reader
   using (OleDbDataReader rdr = cmd_in_params.ExecuteReader())
   {
       //for each customer No  
       while (rdr.Read())
       {
         cmd_stored_proc.Parameters["cust_no"].Value = rdr["cust_no"];
         cmd_stored_proc.ExecuteNonQuery();

         Console.WriteLine("Customer №" + rdr["cust_no"]);
         Console.WriteLine(cmd_stored_proc.Parameters["line1"].Value);
         Console.WriteLine(cmd_stored_proc.Parameters["line2"].Value);
         Console.WriteLine(cmd_stored_proc.Parameters["line3"].Value);
         Console.WriteLine(cmd_stored_proc.Parameters["line4"].Value);
         Console.WriteLine(cmd_stored_proc.Parameters["line5"].Value);
         Console.WriteLine(cmd_stored_proc.Parameters["line6"].Value);
         Console.WriteLine("");
       }
   }

   trans.Commit();
   con.Close();
}
Работа с массивами
ADO .Net может работать с любыми типами данных. Для тех типов Ole Db, у которых нет прямого отображения на типы данных .Net, используется тип данных DBTYPE_VARIANT. Массивы относятся как раз к таким типам.

Следующий пример демонстрирует чтение и запись массива из 5 элементов:

public void ArrayReadWriteTest()
{
    OleDbConnection con = ConnectionProvider.CreateConnection();
    con.Open();
    OleDbTransaction trans = con.BeginTransaction();

    OleDbCommand cmd = new OleDbCommand(
    "select job_code, job_grade, job_country, job_title, language_req from job", 
    con, trans);

    OleDbCommand cmd_upd = new OleDbCommand(
    "update job set language_req=:language_reg where \n" +
    "job_code=:job_code and job_grade=:job_grade and job_country=:job_country", 
    con, trans);

    cmd_upd.Parameters.Add("language_req", OleDbType.Variant);
    cmd_upd.Parameters.Add("job_code", OleDbType.BSTR);
    cmd_upd.Parameters.Add("job_grade", OleDbType.BSTR);
    cmd_upd.Parameters.Add("job_country", OleDbType.BSTR);

    using (OleDbDataReader rdr = cmd.ExecuteReader())
    {
        while (rdr.Read())
        {
        Console.WriteLine("JOB TITLE:" + rdr["job_title"].ToString());

        //чтение массива
        object lang_obj_arr = rdr["language_req"];

        if (lang_obj_arr != DBNull.Value)
        {
            //преобразование к массиву
            //используем Array.CreateInstance для создания массива
        //из 5 элементов, с адресацией начиная с 1-го элемента, а не с 0            
        short arr_lower_bound = 1;

            Array lang_str_arr = Array.CreateInstance(typeof(string), new int[] {5}, new int[] { arr_lower_bound }); 

            //копирование элементов в массив                         
            ((Array)lang_obj_arr).CopyTo(lang_str_arr, arr_lower_bound);

            for (int i = arr_lower_bound; i < lang_str_arr.Length + arr_lower_bound; i++)
            {
              //усечение символа \n на концах элементов массива
  	   string trimmed_value =    
  		   lang_str_arr.GetValue(i).ToString().Replace("\n", "");
              lang_str_arr.SetValue(trimmed_value, i);

              //вывод значения
              if (lang_str_arr.GetValue(i).ToString() != "")
                  Console.WriteLine(lang_str_arr.GetValue(i));
            }

            //запись новых значений элементов массива без символа \n                       
            cmd_upd.Parameters["language_req"].Value = lang_str_arr;
            cmd_upd.Parameters["job_code"].Value = rdr["job_code"];
            cmd_upd.Parameters["job_grade"].Value = rdr["job_grade"];
            cmd_upd.Parameters["job_country"].Value = rdr["job_country"];
        
            //передача изменений в БД
            Assert.IsTrue(cmd_upd.ExecuteNonQuery() == 1);    
         }
         else
            Console.WriteLine("No language specified");

         Console.WriteLine("");
       }
   }

   //откат сделанных изменений         
   trans.Rollback();
   con.Close();
}
ПРИМЕЧАНИЕ.   В примере использован базовый класс Array и метод CreateInstance для создания массива строк. В C# адресация массивов начинается с нулевого элемента, а в данном случае в базе данных записан массив, который проиндексирован, начиная с первого элемента. Array.CreateInstance() позволяет указать нижнюю границу массива элементов. В случае массивов с нулевой адресацией достаточно использования типизированных наследников класса Array, например string[], int[] и т.д.
Работа с BLOB полями

IBProvider поддерживает работу с двумя типами BLOB полей: содержащих текст и бинарные данные. Не могу не заметить, что при использовании этого провайдера работа с BLOB полями происходит так же, как и с обычными типами данных:

public void BLOBReadWriteTest()
{
    OleDbConnection con = ConnectionProvider.CreateConnection();
    con.Open();
    OleDbTransaction trans = con.BeginTransaction();

    //BLOB Read command
    OleDbCommand cmd = new OleDbCommand(
    "select proj_id, proj_name,proj_desc from project", con, trans);

    //BLOB write command			
    OleDbCommand cmd_update = new OleDbCommand(
    "update project set proj_desc=:proj_desc where proj_id=:proj_id", con, trans);
	
    //create parameters with BSTR type
    cmd_update.Parameters.Add("proj_desc", OleDbType.BSTR);
    cmd_update.Parameters.Add("proj_id", OleDbType.BSTR);

    using (OleDbDataReader rdr = cmd.ExecuteReader())
    {
       while (rdr.Read())
       {
		 //чтение BLOB 
             Console.WriteLine("PROJECT: " + rdr["proj_name"].ToString());
             Console.WriteLine(rdr["proj_desc"].ToString());

             //запись BLOB
             cmd_update.Parameters["proj_id"].Value = rdr["proj_id"];

             //каждый раз меняем регистр данных в BLOB поле
             string new_project_description = rdr["proj_desc"].ToString();
             if (new_project_description.ToUpper() != new_project_description)
                 new_project_description = new_project_description.ToUpper();
             else
                 new_project_description = new_project_description.ToLower(); 

             cmd_update.Parameters["proj_desc"].Value = new_project_description;
             Assert.AreEqual(1, cmd_update.ExecuteNonQuery());            
  	    }
    }

    trans.Commit();			
    con.Close();
}

Здесь тип параметров команды обновления установлен в OleDbType.BSTR. В этом случае провайдер корректно распознает тип параметров и произведет их преобразование к типам базы данных.

СОВЕТ.   В примере OleDbDataReader использован совместно с конструкцией using. Он поддерживает интерфейс IDispose и после завершения работы сам позаботится о своем закрытии, а если в метод OleDbCommand.ExecuteReader() передать значение CommandBehavior.CloseConnection, то так же будет закрыто подключение к базе данных.
MARS - Multiple Active Result Sets

В ADO .Net 2.0 появилась «новая» технология, которая получила название MARS. В Net Framework 1.1. в одном контексте транзакции было невозможно держать открытый OleDbDataReader и параллельно выполнять дополнительные запросы к базе данных или открывать ещё один OleDbDataReader. При попытке выполнить этот трюк мы получали исключение вида:

«There is already an open DataReader associated with this Connection which must be closed first.»

Предыдущий пример работы с BLOB полями как раз и показывает применение технологии MARS. В нем демонстрируется последовательное чтение данных и их одновременное их обновление.

Если обратиться к истории, то мы обнаружим, что технология эта совсем не новая, да и технологией назвать это сложно. Если сравнить вторую версию ADO .Net с первой, то, конечно, разработчики добились определенных успехов. Но возможность использовать несколько RecordSet в одной транзакции была реализована ещё в классическом ADO. Скажу больше, там можно было использовать несколько RecordSet, связанных с одной командой. Это достигалось за счет клонирования команды внутри себя, если обнаруживалось, что уже есть связанное с ней множество. В ADO .NET команда тоже умеет клонировать саму себя. Для этого есть метод Clone(), который необходимо вызывать явно, если вы хотите связать несколько OleDbDataReader с одной командой.

Таким образом, применение MARS возможно не только для MS SQL Server, как пишут во многих источниках информации, но и для других баз данных.


Далее
VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...