10.06.2013, 13:17 | #1 |
Участник
|
Работа со сторонней базой через X++
Добрый день.
Пытаюсь реализовать функционал хранения файлов в базе, но не в текущей, с которой работает аос, а со сторонней. Для этого на том же экземпляре SQL создал тестовую базу с 1й таблицей, которая состоит из 2х полей Int64(BigInt) и Container(Image). И пытаюсь записывать туда файлы ссылкой по RecId на DocuValue. В принципе через ADODB, это получилось. Но там нет красивости кода. Поэтому пытаюсь написать полноценную обертку основываясь для работы с SQL на System.Data.SqlClient. И я воткнулся в следующие проблемы. 1. Это как передать в System.Data.SqlClient.SqlParameter BinData. 2. Это перехват исключений По пункту 1 посмотрел темы по работе с ContainerClass, BinData, хочется понять как сама аксапта засовывает контейнеры в SQL именно в поле image, для того чтобы м.б. в дальнейшем сделать можно было расширять обертку. По пункту 2 пробовал пользоваться X++: try { ... } catch(Exception::CLRError) { e = CLRInterop::getLastException(); } Спасибо. |
|
13.06.2013, 08:28 | #2 |
Участник
|
Решение вопросов.
1. Записывать в базу контейнера как аксапта это делает (получается такой же Image) X++: conClass = new ContainerClass(con); binData = new BinData(); binData.setData(conClass.toBlob()); string = binData.base64Encode(); clrObject = System.Convert::FromBase64String(string); //clrObject отправляем в System.Data.SqlClient.SqlParameter.Value 2. Выяснилось что SQL генерирует разные уровни оповещений, и не все являются ошибками, которые перехватывает метод CLRInterop::getLastException(). Также хочу заметить что аксапта не может использовать нетовские handler(обработчик событий. Поэтому Решение - написание библиотеки, которая в себе содержит функции обработки событий и возвращает готовые исключения типа SqlException или Exception, либо это info от sql типа SqlInfoMessageEventArgs X++: public class SysSqlCommand { SqlCommand sqlComm; SqlException sqlEx; Exception ex; public SysSqlCommand(SqlCommand _sqlComm) { this.sqlComm = _sqlComm; } public void ExecuteNonQuery() { try { sqlComm.ExecuteNonQuery(); } catch (SqlException _ex) { sqlEx = _ex; throw; } catch (Exception _ex) { ex = _ex; throw; } } public SqlException sqlException() { return sqlEx; } public Exception exception() { return ex; } } |
|
13.06.2013, 08:31 | #3 |
Участник
|
В дополнение напишу, что п. 1 можно сделать через VARAINT SafeArray, но я не смог разобраться как привести SafeArray - byte[], и мне кажется что это нужно будет делать в dll
|
|
13.06.2013, 19:51 | #4 |
MCT
|
Не прочувствовал, зачем файлы хранить именно в базе? То есть почему просто не использовать допустим файловое хранилице? И почему файлы (правда вы не написали, что за файлы) допустим не хранить в ресурсах системы?
__________________
Axapta book for developer |
|
14.06.2013, 07:02 | #5 |
Участник
|
Как же не написал DocuValue, стандартный функционал выносим в базу.
Получаемые плюсы, 1. Это доступ, если использовать файловое хранилище то каждый пользователь получает доступ. Например если хранятся изображения, то стандартным просмоторщиком можно их смотреть. Открыл изображение и крути себе скрол))). 2. Это бекапирование. Куда проще хранить бекап одного файла чем все эти папки и файлы. 3. Можно контролировать кто открыл файл и держит - стандартный функционал. 4. Так же если документов уж очень много. то возникают баги, тормоза при обслуживании всего этого хозяйства. Найти документ вручную, удалить подменить (хотя спорный момент) Еще возможное использование. Все знаем SysDataBaseLog, которая ну очень большая, и приходится все время что то с ней делать. А тут можно преспокойно вынести в отдельную базу, и не таскать везде ссобой. Самое главное что не теряем контейнеры, которые хранятся в этих 2х таблицах. Ну и наверно еще извращенный мозг сможет придумать применение, при детальном подходе. Последний раз редактировалось Kainix; 14.06.2013 в 07:10. |
|
14.06.2013, 07:06 | #6 |
Участник
|
Не написал как доставать контейнер.
К сожалению не получилось средствами аксапты, не поддерживает синтаксис. Пришлось писать стороннюю функцию X++: public static string getStrFromImg(Object _img) { byte[] image = (byte[])_img; return Convert.ToBase64String(image); } X++: protected container getContainer(int _colNum) { str string = IAU.Functions::getStrFromImg(dataReader.GetValue(_colNum)); ; return ContainerClass::blob2Container(binData::loadFromBase64(string)); } |
|
14.06.2013, 10:38 | #7 |
Участник
|
Нашел метод считывания контейнеров без библиотеки.
X++: protected container getContainer1(int _colNum) { str string ; ; string = CLRInterop::staticInvoke("System.Convert", "ToBase64String", dataReader.GetValue(_colNum)); return ContainerClass::blob2Container(binData::loadFromBase64(string)); } |
|
|
За это сообщение автора поблагодарили: someOne (3). |
15.06.2013, 18:38 | #8 |
Роман Долгополов (RDOL)
|
Цитата:
что мешает вынести таблицы документооборота и SysDataBaseLog в отдельные файловые группы? бекапить/ресторить сможете отдельно, сможете засунуть на другие (более медленные/дешевые/просто другие) диски. делается пяткой левой ноги за 15 минут любым администратором бд без каких либо изменений в DAX |
|
15.06.2013, 19:48 | #9 |
Участник
|
Цитата:
Это доступ, если использовать файловое хранилище то каждый пользователь получает доступ
Цитата:
Это бекапирование. Куда проще хранить бекап одного файла чем все эти папки и файлы.
Цитата:
Можно контролировать кто открыл файл и держит - стандартный функционал.
Цитата:
Так же если документов уж очень много. то возникают баги, тормоза
Цитата:
Все знаем SysDataBaseLog, которая ну очень большая
Ну уж если нужно хранить любой чих в течение ста лет, то можно посмотреть на секционирование таблиц, предлагаемое MS SQL. Правда, если при настроенном секционировании изменить именно SysDataBaseLog, то при синхронизации таблиц, скорее всего, Акса убьет это секционирование и его нужно будет настраивать заново. Но как часто изменяется структура SysDataBaseLog? |
|
|
За это сообщение автора поблагодарили: Kainix (2). |
15.06.2013, 20:28 | #10 |
Участник
|
Ну, а если очень хочется работать именно из Аксапты со сторонней базой, то можно использовать одну из особенностей работы Аксы со своей базой. На данном форуме уже не раз приводился способ работы из Аксы не со своими таблицами, а с вьхами, которые Акса считает своими таблицами. Поищите на форуме соответствующие темы. Основной принцип:
|
|
|
За это сообщение автора поблагодарили: d_alexe (1), arhat (1). |
16.06.2013, 11:43 | #11 |
Участник
|
Спасибо за конструктивную критику, не знал про возможности решения моей проблемы, другими способами, всегда приятно иметь альтернативы. И возможность работать с аксой напрямую с другими базами тоже нужна. Поговорю с админами на счет настройки прав и вынос таблицы в отдельную файловую группу.
|
|
16.06.2013, 11:58 | #12 |
Модератор
|
__________________
-ТСЯ или -ТЬСЯ ? |
|
17.06.2013, 10:23 | #13 |
Участник
|
Обертка для работы со сторонней бд (с поддержкой контенеров)
SharedProject_IAU_SQL (1).xpo - проект IAU.rar - dll Можно без dll, тогда не перехватить сообщения от sql. чтобы все работало зайти в места поменять одноименные методы, класса метод nmExecuteNonQuery - sqlCommandForEx.ExecuteNonQuery(); на sqlCommand.ExecuteNonQuery(); Описание краткое n - net p - property s - set g - get m - method nps - net property Set npg - net property Get nm - net method Вопрос еще такой, как лучше делать обертках, наследовать классы или без наследования. Если наследовать тогда можно избежать методов которые работают с net классами, но тогда нужно следить за инициализацией всех объектов. Если не наследовать то есть методы которые принимают и возвращают net классы. Последний раз редактировалось Kainix; 17.06.2013 в 10:28. |
|
|
За это сообщение автора поблагодарили: raz (5). |
17.06.2013, 10:36 | #14 |
Участник
|
пример использования. Выбор и запись в таблицу, состоящую из 2х полей RefRecId int64(bigint) и BinData container(image).
Для доступа к конкретным полям нужна обертка для System.Data.DataSet X++: static void IAU_Connection(Args _args) { SysSqlConnection sysSqlConnection; SysSqlCommand SysSqlCommand; SysSqlConnectionStringBuilder sysSqlStringBuilder; SysSqlConnectionTable sysSqlConnectionTable; SysDataRowCollection sysDataRowCollection; SysDataRow sysDataRow; SysSqlDataReader sysSqlExecuteReader; System.Exception e; ; try { sysSqlStringBuilder = new SysSqlConnectionStringBuilder(); sysSqlStringBuilder.npsServer(''); sysSqlStringBuilder.npsDataBase(''); sysSqlStringBuilder.npsUserName(''); sysSqlStringBuilder.npsUserPassword(''); sysSqlConnection = new SysSqlConnection(sysSqlStringBuilder.npgConnectionString()); SysSqlCommand = sysSqlConnection.sysSqlCommand(); SysSqlCommand.npsCommandText("SELECT * from Table where RefrecId = @RefRecId"); SysSqlCommand.sysSqlParameterCollection().addSysSqlParameter("@RefRecId", 5637374403); sysSqlExecuteReader = SysSqlCommand.sysSqlExecuteReader(); sysSqlExecuteReader.nmRead(); if(sysSqlExecuteReader.npgHasRows()) { sysSqlExecuteReader.nmClose(); SysSqlCommand = sysSqlConnection.sysSqlCommand(); SysSqlCommand.npsCommandText("Update Table set binData = @BinData where RefrecId = @RefRecId"); SysSqlCommand.sysSqlParameterCollection().addSysSqlParameter("@RefRecId", 5637374403); SysSqlCommand.sysSqlParameterCollection().addSysSqlParameter("@BinData", connull()); SysSqlCommand.nmExecuteNonQuery(); } } catch(Exception::CLRError) { e = CLRInterop::getLastException(); if(e) { while(e) { info(e.get_Message()); e = e.get_InnerException(); } } e = SysSqlCommand.clrSqlExeption(); if(e) { while(e) { info(e.get_Message()); e = e.get_InnerException(); } } else { e = SysSqlCommand.clrExeption(); if(e) { while(e) { info(e.get_Message()); e = e.get_InnerException(); } } } } } Последний раз редактировалось Kainix; 17.06.2013 в 10:40. |
|
Теги |
ado, внешняя база |
|
|