AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 10.06.2013, 13:17   #1  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
Работа со сторонней базой через 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();
}
Объект System.Exception всегда пустой не понятно почему не возвращает, хотя в блок catch попадает.

Спасибо.
Старый 13.06.2013, 08:28   #2  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
Решение вопросов.
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
На выходе получаем byte[], который пригоден для записи в базу.
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  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
В дополнение напишу, что п. 1 можно сделать через VARAINT SafeArray, но я не смог разобраться как привести SafeArray - byte[], и мне кажется что это нужно будет делать в dll
Старый 13.06.2013, 19:51   #4  
MikeR is offline
MikeR
MCT
Аватар для MikeR
MCBMSS
Лучший по профессии 2015
Лучший по профессии 2014
 
1,628 / 627 (24) +++++++
Регистрация: 28.11.2005
Адрес: просто землянин
Не прочувствовал, зачем файлы хранить именно в базе? То есть почему просто не использовать допустим файловое хранилице? И почему файлы (правда вы не написали, что за файлы) допустим не хранить в ресурсах системы?
__________________
Axapta book for developer
Старый 14.06.2013, 07:02   #5  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
Как же не написал DocuValue, стандартный функционал выносим в базу.
Получаемые плюсы,
1. Это доступ, если использовать файловое хранилище то каждый пользователь получает доступ. Например если хранятся изображения, то стандартным просмоторщиком можно их смотреть. Открыл изображение и крути себе скрол))).
2. Это бекапирование. Куда проще хранить бекап одного файла чем все эти папки и файлы.
3. Можно контролировать кто открыл файл и держит - стандартный функционал.
4. Так же если документов уж очень много. то возникают баги, тормоза при обслуживании всего этого хозяйства. Найти документ вручную, удалить подменить (хотя спорный момент)

Еще возможное использование. Все знаем SysDataBaseLog, которая ну очень большая, и приходится все время что то с ней делать. А тут можно преспокойно вынести в отдельную базу, и не таскать везде ссобой.

Самое главное что не теряем контейнеры, которые хранятся в этих 2х таблицах.

Ну и наверно еще извращенный мозг сможет придумать применение, при детальном подходе.

Последний раз редактировалось Kainix; 14.06.2013 в 07:10.
Старый 14.06.2013, 07:06   #6  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
Не написал как доставать контейнер.
К сожалению не получилось средствами аксапты, не поддерживает синтаксис. Пришлось писать стороннюю функцию
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));
}
Где System.Data.SqlClient.SqlDataReader dataReader; IAU.Functions - библиотека с функцией
Старый 14.06.2013, 10:38   #7  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
Нашел метод считывания контейнеров без библиотеки.
X++:
protected container getContainer1(int _colNum)
{
    str     string ;
    ;
    string = CLRInterop::staticInvoke("System.Convert", "ToBase64String", dataReader.GetValue(_colNum));
    return ContainerClass::blob2Container(binData::loadFromBase64(string));
}
Итого библиотека понадобится чтобы обрабатывать сообщений из SQL
За это сообщение автора поблагодарили: someOne (3).
Старый 15.06.2013, 18:38   #8  
db is offline
db
Роман Долгополов (RDOL)
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
 
393 / 692 (24) +++++++
Регистрация: 01.04.2004
Адрес: Москва
Цитата:
Сообщение от Kainix Посмотреть сообщение
Как же не написал DocuValue, стандартный функционал выносим в базу.
Еще возможное использование. Все знаем SysDataBaseLog, которая ну очень большая, и приходится все время что то с ней делать. А тут можно преспокойно вынести в отдельную базу, и не таскать везде ссобой.
бррр... что то не сходится. если причины только в этом, то точно ничего не понимаю

что мешает вынести таблицы документооборота и SysDataBaseLog в отдельные файловые группы? бекапить/ресторить сможете отдельно, сможете засунуть на другие (более медленные/дешевые/просто другие) диски. делается пяткой левой ноги за 15 минут любым администратором бд без каких либо изменений в DAX
Старый 15.06.2013, 19:48   #9  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1293 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Цитата:
Это доступ, если использовать файловое хранилище то каждый пользователь получает доступ
Ну это в том случае, если админы дали полный доступ в каталог хранения файлов. Нормальный админ в этом каталоге запретит перебор состава каталога. Тогда к файлу можно обратиться только по полному имени. Для Аксапты проблем нет, через файловые менеджеры не получится.
Цитата:
Это бекапирование. Куда проще хранить бекап одного файла чем все эти папки и файлы.
Хранить проще. Но бэкапирование должно производится по определенным правилам, которые включают время на операцию. Если все данные в одной файловой группе (или бэкап делается не по файловым группам, а в целом по базе), то все зависит от соотношения полезных данных и редкоменяемых данных в blob полях. Если размер файлов, хранимых в вашей базе, сопоставим по объему с остальными данными, то можем поиметь проблемы. Данные файлов в blob полях меняются редко, а архивировать их нужно вместе со свей базой данных.
Цитата:
Можно контролировать кто открыл файл и держит - стандартный функционал.
Да, можно, но только с одним уточнением "В Аксапте". Если кто-то его открыл извне (из файловой системы или есть какой-то внешний клиент, понимающий формат хранения изображений в базе Аксы), то Акса ничего про это не знает.
Цитата:
Так же если документов уж очень много. то возникают баги, тормоза
Насчет багов не знаю, но тормоза при хранении всех прикрепленных файлов в одном каталоге есть. Когда количество файлов превышает пару десятков тысяч, то управлять каталогом очень сложно. Один просмотр его в любом файловом менеджере может повиснуть на несколько минут. Но тут есть стандартная возможность Аксы распределять разные типы документов по разным каталогам, прямо указывать путь к файлам (при определенных настройках). Ну и очень простая модификация, позволяющая хранить определенные файлы в разных каталогах и на разных серверах (по типам, по времени создания и т.п.). По крайней мере, такая модификация оставляет стандартные возможности и не требует извращений с внешними базами.
Цитата:
Все знаем SysDataBaseLog, которая ну очень большая
Согласен, на этапе запуска и внедрения новой функциональности хочется включить как можно больше логов. Но такие этапы, в конце концов, заканчиваются. Если в режиме эксплуатации включены все возможны логи, то это уже паранойя.
Ну уж если нужно хранить любой чих в течение ста лет, то можно посмотреть на секционирование таблиц, предлагаемое MS SQL. Правда, если при настроенном секционировании изменить именно SysDataBaseLog, то при синхронизации таблиц, скорее всего, Акса убьет это секционирование и его нужно будет настраивать заново. Но как часто изменяется структура SysDataBaseLog?
За это сообщение автора поблагодарили: Kainix (2).
Старый 15.06.2013, 20:28   #10  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1293 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Ну, а если очень хочется работать именно из Аксапты со сторонней базой, то можно использовать одну из особенностей работы Аксы со своей базой. На данном форуме уже не раз приводился способ работы из Аксы не со своими таблицами, а с вьхами, которые Акса считает своими таблицами. Поищите на форуме соответствующие темы. Основной принцип:
  • В AOD создается таблица нужно структуры. Она синхронизируется с базой данных.
  • В базе данный средствами MS SQL таблица удаляется, а вместо неё создается view той же структуры, но в запросе вьюхи обращение идет к нужным данным (не исключено, что на другой сервер), если нужно, то вьюха делается индексируемой. Единственное правило, что если из Аксапты будут идти запросы на обновления, вставку, удаление, то вьюха должна поддерживать эти операции (смотри BOL).
  • Такой подход потребует некоторых изменений в базовых классах синхронизации таблиц, но модификация простая и описана в темах этого форума.
  • После таких манипуляций Акса будет работать так, как будто это её родная таблица, но MS SQL обращаться будет не к таблице, а к вьюхе.
PS: естественно, как любой недокументированный подход, такой способ требует осторожности и четкого документирования для всех заинтересованных лиц. Даже если весь текущий состав ИТ отдела фирмы завтра уволится, следы таких хитростей должны остаться.
За это сообщение автора поблагодарили: d_alexe (1), arhat (1).
Старый 16.06.2013, 11:43   #11  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
Спасибо за конструктивную критику, не знал про возможности решения моей проблемы, другими способами, всегда приятно иметь альтернативы. И возможность работать с аксой напрямую с другими базами тоже нужна. Поговорю с админами на счет настройки прав и вынос таблицы в отдельную файловую группу.
Старый 16.06.2013, 11:58   #12  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
AX 2012 - storing Dynamics AX Document Attachments Within SharePoint
__________________
-ТСЯ или -ТЬСЯ ?
Старый 17.06.2013, 10:23   #13  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
Обертка для работы со сторонней бд (с поддержкой контенеров)
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  
Kainix is offline
Kainix
Участник
 
47 / 96 (4) ++++
Регистрация: 22.11.2007
пример использования. Выбор и запись в таблицу, состоящую из 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, внешняя база

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Взаимодействие с Excel через .NET (семейство классов SysExcel) gl00mie DAX: Программирование 102 11.08.2022 17:09
Ошибка с подключением через ODBC + глюк с работой через connection Corel DAX: Программирование 8 17.05.2012 11:58
Работа с Excel через COM нестабильна на x64 Максимка DAX: Программирование 6 27.01.2012 12:52
работа 1С из Аксапты через COM _scorp_ DAX: Программирование 7 22.08.2008 15:45
Работа с обычными отчетами через Enterprise Portal OliaM DAX: Программирование 0 17.01.2005 16:53

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 08:13.