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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 12.11.2015, 13:13   #1  
sgt.Pepper is offline
sgt.Pepper
Участник
Аватар для sgt.Pepper
 
43 / 15 (1) ++
Регистрация: 05.10.2007
Адрес: Германия
Вызов AIF сервиса в отдельном соединении
Всем добрый день,

есть одна проблема, которая заключается в следующем:
при вызове AIF custom сервиса происходит импорт данных, во время которого пишутся логи. НО: если хоть где-то в коде выбросится ошибка или вызов ttsabort, то соответственно ни лога, ни данных...

Если бы это была одна какая-то таблица, я не задумываясь использовал бы UserConnection, но здесь сервис вызывает класс, который выполняет всю логику, поэтому классу я передать соединение не могу, к тому же класс написан внешниками и зашифрован при помощи макросов (можно конечно парсер написать, но по лицензионному соглашению запрещено).

Есть предложение от коллеги использовать anonymous pipes, примерно вот так:
X++:
        pipeServer = new System.IO.Pipes.AnonymousPipeServerStream(System.IO.Pipes.PipeDirection::Out
                                                                    , System.IO.HandleInheritability::Inheritable);

        thread = new Thread();
        thread.setInputParm([clr2XppStr(pipeServer.GetClientHandleAsString())]);
        thread.removeOnComplete(true);
        thread.run(classNum(ApIpcExample), staticMethodStr(ApIpcExample, messageReader));
        pipeServer.DisposeLocalCopyOfClientHandle();
но не уверен, что в этом случае будет использоваться отдельное соединение.

Может кто сталкивался с подобной проблемой?
Старый 13.11.2015, 00:46   #2  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1234 (44) ++++++++
Регистрация: 11.04.2008
это известная проблема X++, когда выполнение кода просто прерывается без возможности отлова исключений. Чаще всего происходит при вызовах внешнего кода.
Знаю два варианта решения:
#1: На C# пишем простую функцию, которая вызывает внешний сервис (или, в общем случае, любой, потенциально нестабильный внешний код). Функция будет вызывать внешнюю 1:1 как бы вы это делали из X++. Но на C# вы делаете полноценный try/catch, который гарантированно вернет управление обратно в АХ. Функцию подключаем как .net reference в АОТ и юзаем. Это решение простое и надежное, правильное. Но получаем все нюансы использования .net references, что требует бережного обращения, в зависимости от версии АХ
#2 реализовать класс MultithreadHandler, который: 1) запустит необходимое кол-во потоков в асинхронном режиме 2) периодически, опрашивая их статус, дождется их окончания 3) в зависимости от того, вернул поток результат, либо скоропостижно скончался (это можно отследить в классе Thread), залогирует соотвествующий результат. Тут правда, в отличие от первого способа, вы никогда не узнаете, от чего поток умер (или какое исключение вернул внеший вызов)
#3 Гм, или я не понял задачу ,

Последний раз редактировалось DSPIC; 13.11.2015 в 00:49.
Старый 13.11.2015, 10:42   #3  
sgt.Pepper is offline
sgt.Pepper
Участник
Аватар для sgt.Pepper
 
43 / 15 (1) ++
Регистрация: 05.10.2007
Адрес: Германия
Спасибо за совет, идею понял. Если сформулировать точнее, то проблема заключается в следующем:
на вход импорт-класса я последовательно скармливаю XML-и, которые должны в определенной последовательности обрабатываться.

Скажем, у меня есть 5 XML-ей:
подал первую - скушал,
подал вторую - скушал,
на третьей подавился, выбросил ошибку, оборвал транзакцию, в итоге вся информация про 1-ю и 2-ю вместе с логами пропали, поскольку вызов сервиса происходит в контексте одной транзакции.

Значит теоретически я могу, используя метод #2, для каждого прохода открыть новый поток и в нем новое соединение? Или все открываемые потоки будут обрабатываться как одна транзакция?
Старый 16.11.2015, 17:26   #4  
Link is offline
Link
Британский учённый
Аватар для Link
Соотечественники
 
568 / 513 (19) +++++++
Регистрация: 25.11.2005
Адрес: UK
Записей в блоге: 9
Если я правильно понял проблему, то я бы смотрел в сторону модификации AIF - так что бы каждый вызов внешнего класса выполнялся отдельной транзакцией, но это мне видится далеко не тривиальной задачей. К тому же, лог в этом случае, вероятно будет потерян. Другой вариант - изменить принцип логирования\обработки исключений во внешнем классе. Можно, например в случае исключения отсылать лог Аксапте через другой простенький AIF сервис.
__________________
Людям физического труда для восстановления своих сил нужен 7-8 часовой ночной сон. Людям умственного труда нужно спать часов 9-10. Ну а программистов будить нельзя вообще.

Последний раз редактировалось Link; 16.11.2015 в 18:41.
Старый 16.11.2015, 23:50   #5  
Vadik is offline
Vadik
Модератор
Аватар для Vadik
Лучший по профессии 2017
Лучший по профессии 2015
 
3,631 / 1849 (69) ++++++++
Регистрация: 18.11.2002
Адрес: гражданин Москвы
Нельзя несколько вызовов AIF завернуть в некую внешнюю транзакцию. Но, как вариант, можно упаковать несколько документов в одно сообщение - смотрите в сторону Processing batched messages in AIF [AX 2012]
__________________
-ТСЯ или -ТЬСЯ ?
Старый 17.11.2015, 05:26   #6  
Alex_KD is offline
Alex_KD
Участник
AxAssist
MCBMSS
Соотечественники
 
522 / 362 (14) ++++++
Регистрация: 06.07.2006
Адрес: Melbourne, Down Under
Цитата:
можно упаковать несколько документов в одно сообщение
Насколько я понял речь идет про custom сервис, а не стандартные AIF документы.

Цитата:
НО: если хоть где-то в коде выбросится ошибка или вызов ttsabort, то соответственно ни лога, ни данных...
Можно выйти из транзакции которую AIF создает (ttscommit), try/catch вокруг вызова вашего класс и зайти обратно (ttsbegin). Также проверьте appl.ttsLevel(). Класс должен правильно с транзакциями работать (открывать-закрывать).

Посмотрите как AIF вызывает ваш метод в AifWcfProcessor, сделайте трейс.
И как транзакция открывается/закрывается -

AifRequestProcessor.processWcfRequest - AIF делает ttsbegin в конце
AifRequestProcessor.processWcfResponse - AIF делает ttscommit в начале

Имейте ввиду что AIF логика может быть разная для Basic и Enhanced портов.
__________________
AxAssist 2012 - Productivity Tool for Dynamics AX 2012/2009/4.0/3.0
За это сообщение автора поблагодарили: Logger (1), sgt.Pepper (1), A_BAS (2).
Старый 19.11.2015, 15:24   #7  
ex3em is offline
ex3em
Участник
 
38 / 19 (1) ++
Регистрация: 22.07.2008
В методе который выполняется AIF запускайте thread.
Старый 26.04.2016, 20:32   #8  
sgt.Pepper is offline
sgt.Pepper
Участник
Аватар для sgt.Pepper
 
43 / 15 (1) ++
Регистрация: 05.10.2007
Адрес: Германия
Если кому интересно, нашел решение проблемы (спасибо подсказке Alex_KD).

При вызове сервиса AIF создает "глобальную" транзакцию, соттветственно если что-то случается в самом сервисе, все try-catch блоки самого сервиса просто игнорируются и идут выше в обработчики AIF, где собственно было начало транзакции. Все попытки использовать catch Error, ClrError, Internal и просто catch не увенчались успехом.

Что сделано:
в точке входа сервиса сделал проверку на уровень tts и сразу сделал ttscommit.

X++:
ttsInitialLevel = appl.ttsLevel();
if(ttsInitialLevel > 0)
{
    ttscommit;
    ttsbegin;
}
...
Дальше открыл свою транзакцию и запускаю логику. Если код где-то валится, то управление уже не передается выше в AIF, а срабатывают свои обработчики.

После отработки метода делаю ttscommit своей транзакции, и перед возвратом создаю новую.
X++:
if(ttsInitialLevel >0 && appl.ttsLevel() == 0)
ttsbegin;
return something
Так можно обмануть систему.

Последний раз редактировалось sgt.Pepper; 26.04.2016 в 20:43.
За это сообщение автора поблагодарили: gl00mie (2).
Старый 27.04.2016, 13:26   #9  
Dreadlock is offline
Dreadlock
Участник
Аватар для Dreadlock
 
298 / 224 (8) ++++++
Регистрация: 07.05.2009
Адрес: Москва
Цитата:
Сообщение от sgt.Pepper Посмотреть сообщение
Если кому интересно, нашел решение проблемы (спасибо подсказке Alex_KD)...
Немного доработайте свой код, т.к. ttsLevel может быть и > 1
За это сообщение автора поблагодарили: gl00mie (2).
Старый 27.04.2016, 15:52   #10  
sgt.Pepper is offline
sgt.Pepper
Участник
Аватар для sgt.Pepper
 
43 / 15 (1) ++
Регистрация: 05.10.2007
Адрес: Германия
Ну я собственно с единицей нигде не сравниваю, проверяю только, что транзакция открыта (ttsInitialLevel > 0)
А в чем вы видите ошибку?
Старый 27.04.2016, 15:55   #11  
Dreadlock is offline
Dreadlock
Участник
Аватар для Dreadlock
 
298 / 224 (8) ++++++
Регистрация: 07.05.2009
Адрес: Москва
ttsBegin у вас один, когда все транзакции возвращаетесь
Старый 27.04.2016, 16:44   #12  
sgt.Pepper is offline
sgt.Pepper
Участник
Аватар для sgt.Pepper
 
43 / 15 (1) ++
Регистрация: 05.10.2007
Адрес: Германия
Вы правы, надо будет что-то вроде такого написать
X++:
for (i==1; I<= ttsInitialLevel;i++)
    ttsbegin;
Только я ни разу не заметил начального уровня больше чем 1. Думаю он открывается в AIF только один раз, но на всякий случай проверю.
Старый 27.04.2016, 17:35   #13  
Dreadlock is offline
Dreadlock
Участник
Аватар для Dreadlock
 
298 / 224 (8) ++++++
Регистрация: 07.05.2009
Адрес: Москва
Цитата:
Сообщение от sgt.Pepper Посмотреть сообщение
Вы правы, надо будет что-то вроде такого написать
X++:
for (i==1; I<= ttsInitialLevel;i++)
    ttsbegin;
Только я ни разу не заметил начального уровня больше чем 1. Думаю он открывается в AIF только один раз, но на всякий случай проверю.
Береженого бог бережет
Сегодня Вы не видите несколько транзакций, сделаете как вам видится, уволитесь, пройдет 5ть лет и кто-нибудь влезет в этот механизм, потом пол компании будет затылок чесать: "а что же произошло"?

P.S. Может вместо ttsAbort написать ttsCommit?
Старый 27.04.2016, 17:44   #14  
sgt.Pepper is offline
sgt.Pepper
Участник
Аватар для sgt.Pepper
 
43 / 15 (1) ++
Регистрация: 05.10.2007
Адрес: Германия
Вообще-то я нигде не писал ttsAbort, а при перехвате исключения ttsLevel итак сбрасывается на 0.
А через 5 лет наверно и X++ уже не будет.
Старый 29.04.2016, 20:06   #15  
Dreadlock is offline
Dreadlock
Участник
Аватар для Dreadlock
 
298 / 224 (8) ++++++
Регистрация: 07.05.2009
Адрес: Москва
Цитата:
Сообщение от sgt.Pepper Посмотреть сообщение
Вообще-то я нигде не писал ttsAbort, а при перехвате исключения ttsLevel итак сбрасывается на 0.
Упс, писал в метро на скорую руку

Цитата:
Сообщение от sgt.Pepper Посмотреть сообщение
А через 5 лет наверно и X++ уже не будет.
Поверьте мне, Ах стоит в компаниях годами, я сам сейчас на проекте, где Ах 3.0 уже 16 лет исправно служит добрую службу.
Старый 19.05.2016, 12:44   #16  
Link is offline
Link
Британский учённый
Аватар для Link
Соотечественники
 
568 / 513 (19) +++++++
Регистрация: 25.11.2005
Адрес: UK
Записей в блоге: 9
Вот еще по теме: Preserve data when AIF service call fails

Dynamics AX AIF is great for all kind of interfaces and integration with 3rd party applications. By default AIF has an built-in transaction mechanism that prevents your system to become inconsistent if something goes wrong during processing.

Here is an example of a simple method which writes message data and the current transaction level into a table

X++:
    [SysEntryPointAttribute]
    public void call(Description _message)
    {
        ERPServiceTable serviceTable;

        serviceTable.Message = _message;
        serviceTable.Info = strFmt("%1", appl.ttsLevel());
        serviceTable.insert();
    }
Here is the service call from a C# console application

PHP код:
    var client = new ERPService.ERPServiceClient();
    
client.ClientCredentials.Windows.ClientCredential.Domain "YOUR_DOMAIN";
    
client.ClientCredentials.Windows.ClientCredential.UserName "YOUR_USER";
    
client.ClientCredentials.Windows.ClientCredential.Password "YOUR_PASSWORD";

    var 
context = new ERPService.CallContext();
    
context.MessageId Guid.NewGuid().ToString();
    
client.call(context"Service Call"); 
When you call the service the result looks like this: Notice, the transaction level is already 1 although there is no ttsbegin in code.

AIF service call

But what happens when something goes wrong during processing the service call (e.g. posting a journal fails). Here is as simple method which can be used to simulate such a situation:

X++:
    [SysEntryPointAttribute]
    public void callWithError(Description _message,boolean _error)
    {
        this.call(_message);
        if(_error)
        {
            throw error("Exception!");
        }
    }
When you call the service method with the error flag set to FALSE, the message is written to the table and stays there.

X++:
    context.MessageId = Guid.NewGuid().ToString();
    client.callWithError(context, "Service Call without error", false);
However if you call the method with the error flag set to TRUE a rollback is triggered and your data is gone!

X++:
    try
    {
            context.MessageId = Guid.NewGuid().ToString();
            client.callWithError(context, "Service Call 2 with error", true);
    }
    catch
    {
            Console.WriteLine("The service failed as expected!");
    }
The table contains only 2 entries for both successful service calls. The data from the third service call is gone.

Service call with error not saved

However, if you want to keep the initial data for a post mortem analysis, this behavior is a problem. One way to work around this rollback is to reduce the transaction level back to 0 by calling an additional ttscommit. This will ensure no rollback will delete the transmitted data. Finally, raise the ttslevel back to 1 for further processing.

X++:
    [SysEntryPointAttribute]
    public void callWithError(Description _message,boolean _error = false)
    {
        this.call(_message);

        ttsCommit;
        ttsBegin;
       
        if(_error)
        {
            throw error("Exception!");
        }
    }
When you call the service method with the error flag set to true, the method still fails. But the initial transmitted data is preserved and can be used for analysis or manual processing.

Service call with error preserverd
BTW.

A more relaxed way to address this issue is to separate interfaces from business logic. For example provide two methods, one to transmit the data and another to process the data after it was successfully transmitted. If a second method is not an option (for what reason ever) write a batch class which processes the data. However, if you require immediate processing and a second call is not feasible this workaround may help.
__________________
Людям физического труда для восстановления своих сил нужен 7-8 часовой ночной сон. Людям умственного труда нужно спать часов 9-10. Ну а программистов будить нельзя вообще.
За это сообщение автора поблагодарили: Logger (1).
Теги
aif, ax2012, user connection

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Вызов веб-сервиса Ax 2012 R3 из 1C 8.2 Bega DAX: Программирование 8 06.02.2015 13:30
Вызов Web-сервиса из Ax2009 samolalex DAX: Программирование 6 21.11.2012 14:50
daxdilip: How to: Configure Dynamics AX AIF Services to listen for SSL Requests (https) Blog bot DAX Blogs 0 23.01.2011 10:12
Dianne Siebold: AIF Top Ten Blog bot DAX Blogs 1 22.04.2008 11:19
Вызов веб-сервиса из аксапты dzeaman DAX: Программирование 18 24.12.2005 13:22
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

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

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

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 14:00.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.