Обновление 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:
На этом пока все. Всем пока! 🙂