AXForum  
Zurück   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Kennwort vergessen?
Registrieren Forum Rules Hilfe Benutzerliste Heutige Beiträge Suchen

 
 
Themen-Optionen Thema durchsuchen Ansicht
Alt 06.06.2022, 12:40   #1  
SuperStar88 ist offline
SuperStar88
Участник
 
82 / 10 (1) +
Registriert seit: 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;
Как споймать ошибки в моей функции/запросе и откатывать обновления?
Alt 06.06.2022, 13:03   #2  
axm2017 ist offline
axm2017
Участник
 
2.067 / 296 (14) ++++++
Registriert seit: 15.05.2017
Может сделать запрос реквеста вне транзакции?
И уж если он отработал то что то делать
Alt 06.06.2022, 13:21   #3  
sukhanchik ist offline
sukhanchik
Administrator
Benutzerbild von sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3.343 / 3563 (125) ++++++++++
Registriert seit: 13.06.2004
Ort: Москва
"В лоб" нет ответа. Классический аналог задачи - в транзакцию запихнуть отправку на электронную почту письма. При этом на вопрос "откатываем ли транзакцию, если почтовый сервер стал недоступен?" обычно отвечают, что "ну мы же не можем останавливать работу системы из-за того, что где-то какой-то сервер, которым мы не управляем - стал недоступен"

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

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

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

Geändert von sukhanchik (06.06.2022 um 13:23 Uhr)
Alt 06.06.2022, 13:26   #4  
Товарищ ♂uatr ist offline
Товарищ ♂uatr
Участник
Benutzerbild von Товарищ ♂uatr
MCBMSS
 
340 / 931 (32) +++++++
Registriert seit: 23.10.2012
Добрый день.
Транзакцию можно обернуть внутри иного user connection'a.
Alt 06.06.2022, 18:59   #5  
Logger ist offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
4.001 / 3298 (118) ++++++++++
Registriert seit: 12.10.2004
Ort: Москва
Blog-Einträge: 2
Коллега (glibs) подсказал как-то хороший трюк для таких случаев.
Можно ваш код вынести в отдельный метод и дернуть его через runAs тогда он отработает в отдельной X++ сессии в которой будет своей соединение к БД. И если будет исключение то его можно будет обработать - исходная ваша транзакция не откатится.

Но надо аккуратно быть с обновлениями - можно в блокировку попасть.
т.е. нежелательно чтобы этот ваш метод что-то обновлял.
This post has been rated by: dim-gin (1), sukhanchik (4).
Alt 06.06.2022, 19:49   #6  
dim-gin ist offline
dim-gin
Участник
 
41 / 30 (2) +++
Registriert seit: 15.04.2014
Ort: СПб
Раз уж зашла речь про транзакции и откаты, можно немного оффтопом задать вопрос: влияет ли установленный через Connection XACT_ABORT = ON (и не возвращённый в OFF) на работу клиента аксапты?
Alt 07.06.2022, 00:23   #7  
skuull ist offline
skuull
Участник
Most Valuable Professional
Лучший по профессии 2014
 
700 / 752 (27) +++++++
Registriert seit: 08.03.2013
Ort: ХЗ
Можно просто вынести это в C# библотеку и там обрабатывайте все исключения, что намного удобнее чем в х++
Alt 08.06.2022, 09:48   #8  
DarkSpirit22 ist offline
DarkSpirit22
Участник
Benutzerbild von DarkSpirit22
 
13 / 94 (4) ++++
Registriert seit: 07.11.2013
Ort: СПб
Я сделал 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;
}
Alt 11.06.2022, 19:16   #9  
wojzeh ist offline
wojzeh
Участник
Benutzerbild von wojzeh
Соотечественники
 
681 / 517 (19) +++++++
Registriert seit: 27.04.2006
Ort: 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
 

Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
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

Forumregeln
Es ist Ihnen nicht erlaubt, neue Themen zu verfassen.
Es ist Ihnen nicht erlaubt, auf Beiträge zu antworten.
Es ist Ihnen nicht erlaubt, Anhänge hochzuladen.
Es ist Ihnen nicht erlaubt, Ihre Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Gehe zu

Рейтинг@Mail.ru
Alle Zeitangaben in WEZ +3. Es ist jetzt 12:20 Uhr.
Powered by vBulletin® Version 3.8.5 (Deutsch)
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.