Обновление NetProvider [build 1183]. Поддержка System.IO.Stream и System.IO.TextReader
Привет всем.
На сайт загружена новая сборка (1183) .NET провайдера, в которой реализована полноценная поддержка объектов System.IO.Stream и System.IO.TextReader. Объекты этих классов можно:
- передавать в качестве IN-значений параметров команды
- получать через OUT-значения параметров команды
- получать через методы GetStream и GetTextReader класса OleDbDataReader
- использовать как для обычных колонок, так и для колонок с массивами.
При передаче и при получении объектов этих классов используются механизмы «поточной обработки данных». Иными словами — теперь через Stream и TextReader можно перекачивать любые объемы информации.
Поддержка System.IO.Stream
При работе с объектами класса System.IO.Stream, NetProvider задействует стандартный COM-интерфейс ISequentialStream. COM-объекты с этим интерфейсом передаются в IBProvider и получаются из него.
Поддержка System.IO.TextReader
При работе с объектами класса System.IO.TextReader, NetProvider задействует специализированный COM-интерфейс IIBP_SequentialStream_WideChar. COM-объекты с этим интерфейсом передаются в IBProvider и получаются из него.
Передача объектов в качестве IN-значений параметров
Здесь нет никаких тонкостей — нужно просто установить объекты в OleDbParameter.Value. И все. Дальше NetProvider сам выполнит все необходимые операции по упаковке этих .NET объектов в COM-объекты и передаст последние в OLEDB провайдер.
Единственное что стоит отметить — NetProvider не вызывает Dispose у установленных объектов после завершения работы с ними.
Получение объектов через OUT-значения параметров
Для получения объектов через OUT-значения параметров, требуется дополнительное конфигурирование со стороны пользовательского приложения:
//using xdb=lcpi.data.oledb;
//xdb.OleDbCommand cmd;
//cmd.Parameters[0] связан с OUT-параметром запроса
//Получение объекта с интерфейсом System.IO.Stream
cmd.Parameters[0].OutputBinding.Set(xdb.OleDbType.IUnknown,
typeof(System.IO.Stream);
//Получение объекта с интерфейсом System.IO.TextReader
cmd.Parameters[0].OutputBinding.Set(xdb.OleDbType.IUnknown,
typeof(System.IO.TextReader);
//Получение массива с объектами System.IO.Stream
cmd.Parameters[0].OutputBinding.Set(xdb.OleDbType.Array_IUnknown,
typeof(System.IO.Stream);
//Получение массива с объектами System.IO.TextReader
cmd.Parameters[0].OutputBinding.Set(xdb.OleDbType.Array_IUnknown,
typeof(System.IO.TextReader);
Первый аргумент, с определением OLEDB типа, передается в OLEDB провайдер, и указывает последнему что нужно вернуть COM-объект с представлением значения параметра команды. Тип, указываемый во втором аргументе, определяет класс объекта (или элемента массива), который должен вернуть .NET провайдер, и влияет на интерфейс запрашиваемого COM-объекта.
Получение объектов для значений колонок
DbDataReader из .NET 4.5 содержит два новых виртуальных метода GetStream и GetTextReader, которые переопределяются в OleDbDataReader нашего .NET провайдера.
В случае сборок под предыдущие версии .NET (3.5, 4.0), GetStream и GetTextReader определены как обычные (не виртуальные) методы класса OleDbDataReader.
Пример с IN/OUT параметрами
Давно хотелось написать маленький и лаконичный пример, который бы загружал в базу данных реальный файл. Если исключить «служебный» код в ниже приведенном примере, думаю, что мне это удалось.
В данном примере выполняется запрос «INSERT… RETURNING…» с двумя параметрами: IN и OUT. Через IN-параметр передается объект System.IO.FileStream. А через OUT-параметр получаем обратно наши данные в виде потока, содержимое которого потом сравнивается с оригиналом.
Отмечу, что в запросе используется нестандартное расширение IBProvider-а — «RETURNING… INTO…», позволяющее явно указать имя для OUT-параметра. После добавления этой функциональности, я реально на неё подсел и повсеместно использую её в своих тестах 🙂
using System;
using System.Data;
using xdb=lcpi.data.oledb;
namespace Sample_001__BlobAndStream
{
class Program
{
private const string c_cn_str
="provider=LCPI.IBProvider.3;"
+"location=localhost:d:\\database\\ibp_test_fb25_d3.gdb;"
+"user id=gamer;"
+"password=vermut;"
+"named_param_prefix=@;"
+"named_param_rules=1";
private const string c_sql
="insert into BIN_BLOB_TABLE (BIN_DATA) values (@in)\n"
+"returning BIN_DATA\n"
+"into @out";
//883 229 976 bytes
private const string c_src_file
="d:\\Distrib\\MSSQL\\MS SQL 2008 Express\\"
+"ru_sql_server_2008_r2_express_with_advanced_services_x64.exe";
static void Main(string[] args)
{
try
{
Console.WriteLine("Connect to database...");
using(var cn=new xdb.OleDbConnection(c_cn_str))
{
cn.Open();
Console.WriteLine("Begin transaction ...");
using(var tr=cn.BeginTransaction(IsolationLevel.RepeatableRead))
{
using(var cmd=new xdb.OleDbCommand(c_sql,cn,tr))
{
var stream1=new System.IO.FileStream(c_src_file,
System.IO.FileMode.Open);
cmd["@in"].Value
=stream1;
cmd["@out"].OutputBinding.Set(xdb.OleDbType.IUnknown,
typeof(System.IO.Stream));
//-----
Console.WriteLine("Execute command [press any key]...");
Console.ReadKey(false);
cmd.ExecuteNonQuery();
//-----
stream1.Dispose();
//-----
Console.WriteLine("Get streams ...");
stream1=new System.IO.FileStream(c_src_file,
System.IO.FileMode.Open);
var stream2=(System.IO.Stream)cmd["@out"].Value;
//-----
Console.WriteLine("out value type: {0}",
stream2.GetType().FullName);
//-----
Console.WriteLine("Compare data [press any key]...");
Console.ReadKey(false);
const int c_buf_size=256*1024;
var buf1=new byte[c_buf_size];
var buf2=new byte[c_buf_size];
long total_cb=0;
for(;;)
{
var c1=stream1.Read(buf1,0,c_buf_size);
var c2=stream2.Read(buf2,0,c_buf_size);
if(c1!=c2)
throw new ApplicationException("not equal block sizes!");
for(int i=0;i!=c1;++i)
{
if(buf1[i]!=buf2[i])
throw new ApplicationException("not equal elements!");
}//for i
total_cb+=c1;
if(c1==0)
break;
}//for
stream1.Dispose();
stream2.Dispose();
Console.WriteLine("Total byte count: {0}",total_cb);
}//using cmd
Console.WriteLine("Commit transaction...");
tr.Commit();
}//using tr
}//using cn
}
catch(Exception e)
{
Console.WriteLine("");
Console.WriteLine("ERROR: {0} - {1}",e.Source,e.Message);
}//catch
Console.WriteLine("DONE [press any key]");
Console.ReadKey(false);
}//Main
}//class Program
}//namespace Sample_001__BlobAndStream
Вывод:

Ход выполнения программы в Process Explorer-e:

На этом пока все. Всем пока! 🙂