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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 06.06.2022, 12:40   #1  
SuperStar88 is offline
SuperStar88
Участник
 
82 / 10 (1) +
Регистрация: 11.08.2017
? DAX 2012. Как споймать ошибку try catch внутри транзакции?
Всем привет!
У меня при обновлении таблицы идёт запрос WebRequest к внешней службе, чтобы и там обновить данные
X++:
public void update()
{
ttsBegin;
super();

this.MyRequest(this);

ttsCommit;
И если при запросе/ответе возникает ошибка, то блок catch внутри моей функции не вызывается.
Функция имеет вид наподобии такого:
X++:
    str                             responseString;
    
    System.Net.WebRequest           webRequest;
    System.Net.HttpWebResponse      httpResponse;
    CLRObject                       responseObj;
    System.IO.Stream                stream;
    System.IO.StreamReader          streamReader;
    System.Exception                ex;
    System.Net.WebException         webException;
    ;

    try
    {
        if (!urlAPI)
            throw error("Error");

        codeAccessPermission::revertAssert();
        new InteropPermission(InteropKind::ClrInterop).assert();

        webRequest = System.Net.WebRequest::Create(urlAPI);
        webRequest.set_Method('POST');

        stream = webRequest.GetRequestStream();
        stream.Write(arrayOfBytes,0,arrayOfBytes.get_Length());
        stream.Close ();

        httpResponse    = webRequest.GetResponse();
    }
    catch (Exception::CLRError)
    {
        ex = ClrInterop::getLastException();
        if (ex != null)
        {
            ex = ex.get_InnerException();
            if ((ex != null) && (ex is System.Net.WebException))
            {
                webException    = ex as System.Net.WebException;
                responseObj     = webException.get_Response();
                httpResponse    = responseObj as System.Net.HttpWebResponse;
            }
        }
    }
    catch
    {
        error("Error");
        return '';
    }
    stream          = httpResponse.GetResponseStream();
    streamReader    = new System.IO.StreamReader(stream);
    responseString  = streamReader.ReadToEnd();

    streamReader.Close();
    stream.Close();
    httpResponse.Close();

    codeAccessPermission::revertAssert();

    return responseString;
Как споймать ошибки в моей функции/запросе и откатывать обновления?
Старый 06.06.2022, 13:03   #2  
axm2017 is offline
axm2017
Участник
 
1,917 / 313 (14) ++++++
Регистрация: 15.05.2017
Может сделать запрос реквеста вне транзакции?
И уж если он отработал то что то делать
Старый 06.06.2022, 13:21   #3  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,322 / 3547 (125) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
"В лоб" нет ответа. Классический аналог задачи - в транзакцию запихнуть отправку на электронную почту письма. При этом на вопрос "откатываем ли транзакцию, если почтовый сервер стал недоступен?" обычно отвечают, что "ну мы же не можем останавливать работу системы из-за того, что где-то какой-то сервер, которым мы не управляем - стал недоступен"

Поэтому ответ здесь зависит от ситуации. Что вероятнее может случиться - ошибка и откат транзакции или ошибка при веб-запросе?
Вариант 1. Ошибка и откат транзакции. В этом случае:
1. Делаем проверку на то, что ошибки не будет. Для этого - запускаем на исполнение этот же код, но в конце вызываем ttsabort. Все выборки на обновление делаем с пессимистической блокировкой для гарантии исключения конфликтов совместного обновления. Если код дошёл до конца - то вызываем ttsabort.
2. (Транзакция не открыта). Вызываем веб-запрос. Если вернулась ошибка - не вызываем код с транзакцией. Если ошибки нет - то вызываем код с транзакцией, пессимистическими блокировками и уже делаем ttscommit;

Вариант 2. Ошибка при веб-запросе. Здесь надо "на берегу" договориться - останавливаем ли мы работу системы до тех пор, пока веб-запрос не перестанет возвращать ошибки (пример с почтовым сервером) или идем дальше, а с веб-запросом будем разбираться отдельно?
Если останавливаем - то тогда отрабатываем по варианту 1.
Если не останавливаем, то тогда сначала вызываем код в транзакции и только после его успешной отработки - вызываем веб-запрос.
Если веб-запрос выдает ошибки - то эти ошибки логируем и готовим какой-нибудь пакетник по массовой обработки накопившихся ошибок (пример - массовая рассылка почты по тем письмам, которые не были отправлены).

Ну собственно - здесь основной вопрос и есть - что делать системе, если по той или иной причине не получается отработать веб-запрос
__________________
Возможно сделать все. Вопрос времени

Последний раз редактировалось sukhanchik; 06.06.2022 в 13:23.
Старый 06.06.2022, 13:26   #4  
Товарищ ♂uatr is offline
Товарищ ♂uatr
Участник
Аватар для Товарищ ♂uatr
MCBMSS
 
305 / 873 (30) +++++++
Регистрация: 23.10.2012
Добрый день.
Транзакцию можно обернуть внутри иного user connection'a.
Старый 06.06.2022, 18:59   #5  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Коллега (glibs) подсказал как-то хороший трюк для таких случаев.
Можно ваш код вынести в отдельный метод и дернуть его через runAs тогда он отработает в отдельной X++ сессии в которой будет своей соединение к БД. И если будет исключение то его можно будет обработать - исходная ваша транзакция не откатится.

Но надо аккуратно быть с обновлениями - можно в блокировку попасть.
т.е. нежелательно чтобы этот ваш метод что-то обновлял.
За это сообщение автора поблагодарили: dim-gin (1), sukhanchik (4).
Старый 06.06.2022, 19:49   #6  
dim-gin is offline
dim-gin
Участник
 
41 / 30 (2) +++
Регистрация: 15.04.2014
Адрес: СПб
Раз уж зашла речь про транзакции и откаты, можно немного оффтопом задать вопрос: влияет ли установленный через Connection XACT_ABORT = ON (и не возвращённый в OFF) на работу клиента аксапты?
Старый 07.06.2022, 00:23   #7  
skuull is offline
skuull
Участник
Most Valuable Professional
Лучший по профессии 2014
 
700 / 752 (27) +++++++
Регистрация: 08.03.2013
Адрес: ХЗ
Можно просто вынести это в C# библотеку и там обрабатывайте все исключения, что намного удобнее чем в х++
Старый 08.06.2022, 09:48   #8  
DarkSpirit22 is offline
DarkSpirit22
Участник
Аватар для DarkSpirit22
 
13 / 94 (4) ++++
Регистрация: 07.11.2013
Адрес: СПб
Я сделал axapta-vs-c# проект WebRequestNet с оберткой:
X++:
using System;
using System.Net;

namespace WebRequestNet
{
    public class HttpWebRequestExceptionSafe
    {
        public HttpWebRequest HttpWebRequest { get; private set; }
        protected HttpWebRequestExceptionSafe(HttpWebRequest _httpWebRequest)
        {
            this.HttpWebRequest = _httpWebRequest;
        }

        public static HttpWebRequestExceptionSafe Create(Uri requestUri)
        {
            return new HttpWebRequestExceptionSafe((HttpWebRequest)HttpWebRequest.Create(requestUri));
        }

        public static HttpWebRequestExceptionSafe Create(string requestUriString)
        {
            return new HttpWebRequestExceptionSafe((HttpWebRequest)HttpWebRequest.Create(requestUriString));
        }

        public HttpWebResponse GetResponseSafe()
        {
            try
            {
                return this.HttpWebRequest.GetResponse() as HttpWebResponse;
            }
            catch (WebException ex)
            {
                HttpWebResponse response = ex.Response as System.Net.HttpWebResponse;

                if (response == null)
                    throw;

                return response;
            }
        }
    }
}
И на примере метода RetailCommonWebAPI.getResponse это будет выглядеть так:

X++:
/// <summary>
/// Get retail web access request.
/// </summary>
/// <param name="_request">
/// Retail web request.
/// </param>
/// <returns>
/// Web response to access.
/// </returns>
public RetailWebResponse getResponse(RetailWebRequest _request)
{
    System.Net.HttpWebRequest       request = null;
    System.Net.HttpWebResponse      response = null;
    CLRObject                       webResponse;
    System.Net.WebHeaderCollection  headers;
    System.IO.MemoryStream          stream;
    Binary                          requestContent;
    str                             responseData;
    System.Exception                ex;
    System.Net.WebException         webException;
    RetailWebResponse               retailWebResponse;
    int                             httpStatusCode;
    str                             contentType;
    MapEnumerator                   headerMapEnumerator;

        //+ Abramov_ 26.04.2022 TSK0000262_02
    WebRequestNet.HttpWebRequestExceptionSafe requestAdapter;
    str                                       exceptionMessage;
    System.Exception                          innerException;
    System.Net.WebExceptionStatus             webExceptionStatus;
    //- Abramov_ 26.04.2022 TSK0000262_02
    ;

    try
    {
        //+ Abramov_ 20.04.2022 TSK0000262_02
        //request = System.Net.WebRequest::Create(_request.parmUrl()) as System.Net.HttpWebRequest;
        requestAdapter = WebRequestNet.HttpWebRequestExceptionSafe::Create(_request.parmUrl());
        request        = requestAdapter.get_HttpWebRequest();
        //- Abramov_ 20.04.2022 TSK0000262_02

        if (strLen(_request.parmMethod()) > 0)
        {
            request.set_Method(_request.parmMethod());
        }

        headers = request.get_Headers();
        //+ Abramov_ 19.11.2021 TSK0000009_01
        /*
        if (strLen(_request.parmHeader()) > 0)
        {
            headers.Add(_request.parmHeader());
        }
        */
        headerMapEnumerator = _request.getHeaders().getEnumerator();
        while (headerMapEnumerator.moveNext())
        {
            headers.Add(any2str(headerMapEnumerator.currentKey()), any2str(headerMapEnumerator.currentValue()));
        }
        //- Abramov_ 19.11.2021 TSK0000009_01

        if (strLen(_request.parmContentType()) > 0)
        {
            request.set_ContentType(_request.parmContentType());
        }

        requestContent = _request.parmContent();
        if (requestContent)
        {
            stream = requestContent.getMemoryStream();
            RetailCommonWebAPI::writeRequestData(request, stream.ToArray());
        }

        //+ Abramov_ 19.11.2021 TSK0000009_01
        if (_request.parmKeepAlive())
            request.set_KeepAlive(CLRInterop::getObjectForAnyType(_request.parmKeepAlive()));
        //- Abramov_ 19.11.2021 TSK0000009_01

        //+ Abramov_ 25.04.2022 TSK0000264_01
        if (_request.parmTimeout())
            request.set_Timeout(_request.parmTimeout());
        //- Abramov_ 25.04.2022 TSK0000264_01

        //+ Abramov_ 20.04.2022 TSK0000262_02
        // Вызов "безопасной" версии метода GetResponse, который не генерирует исключение при получении ответа от сервера, но с кодом отличным от 200.
        /*
        webResponse = request.GetResponse();
        response = webResponse as System.Net.HttpWebResponse;
        */
        response = requestAdapter.GetResponseSafe();
        //- Abramov_ 20.04.2022 TSK0000262_02
    }
    catch (Exception::CLRError)
    {
        ex = ClrInterop::getLastException();

        if (ex != null && ex.get_InnerException() != null)
        {
            innerException = ex.get_InnerException();

            if (innerException is System.Net.WebException)
            {
                webException = innerException as System.Net.WebException;

                //+ Abramov_ 26.04.2022 TSK0000262_02
                //Обработка случая, когда Response "сидит" в WebException, находится в методе requestAdapter.GetResponseSafe();

                webExceptionStatus = webException.get_Status();
                if (webExceptionStatus == System.Net.WebExceptionStatus::Timeout)
                {
                    error(innerException.get_Message());
                    throw Exception::Timeout;
                }
                else
                {
                        throw error(innerException.get_Message());
                }
                //- Abramov_ 26.04.2022 TSK0000262_02
            }
        }

        //+ Abramov_ 26.04.2022 TSK0000262_02
        innerException = ex.get_InnerException();

        while (innerException)
        {
            // BP deviation documented
            if (exceptionMessage)
                exceptionMessage += '\n';

            exceptionMessage += exceptionMessage + " " + CLRInterop::getAnyTypeForObject(innerException.get_Message());
            innerException = innerException.get_InnerException();
        }

        throw error(exceptionMessage);
        //- Abramov_ 26.04.2022 TSK0000262_02
    }

    responseData = RetailCommonWebAPI::readResponseData(response);
    response.Close();

    httpStatusCode = response.get_StatusCode();

    contentType = response.get_ContentType();
    retailWebResponse = new RetailWebResponse(httpStatusCode, responseData, contentType);

    return retailWebResponse;
}
Старый 11.06.2022, 19:16   #9  
wojzeh is offline
wojzeh
Участник
Аватар для wojzeh
Соотечественники
 
674 / 512 (19) +++++++
Регистрация: 27.04.2006
Адрес: Montreal
X++:
// to write your log from inside of another transaction
	static public void insertExtEventLogInSeparateConnection(RefRecId _lineRecId, str _guid, str _logSource, str _logStr)
    {
        ExtEventLog         log;
        UserConnection      connection;
        int                 ttsLevel    = appl.ttsLevel(); //here you can check if you are inside of a transaction
        str                 errMsg      = strFmt("Cannot add event log '%1:%2:%3'", _guid, _logSource, _logStr);
		// let's create a separate connection
        try
        {
            connection = new UserConnection();
            connection.ttsbegin();
            log.setConnection(connection);
            log.InstructionDocLineRecId = _lineRecId;
            log.TaskGUID                = _guid;
            log.LogStr                  = _logStr;
            log.LogSource               = _logSource;

            log.doInsert();
            connection.ttscommit();
        }
        catch
        {
            throw error(errMsg);
        }
        finally
        {
            if(connection)
            {
                connection.finalize();
            }
        }
    }
__________________
Felix nihil admirari
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
emeadaxsupport: EHF – eInvoice for Norway – DAX 2012 – setup Reviewed 31012016 Blog bot DAX Blogs 0 22.02.2016 17:11
amer-ax: It was a great day! Blog bot DAX Blogs 3 29.12.2012 01:02
dynamicsaxtraining: Purchase Blog bot DAX Blogs 0 11.03.2012 05:25
ChangeCompany Try...Catch Владимир Максимов DAX: Программирование 10 12.01.2009 17:19
Глупый вопрос про try .. catch Vadik DAX: База знаний и проекты 6 12.03.2003 18:04
Опции темы Поиск в этой теме
Поиск в этой теме:

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

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

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

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