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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 11.11.2023, 18:37   #1  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1293 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
DAX2012 тип dot net System.Object и тип X++ real с CIL как правильно работать?
Добрый день.
Есть работа с внешней базой, используется System.Data.SqlClient.SqlDataReader.

Его свойство Item[...] (в Аксе используем метод get_Item(...)) возвращает System.Object.
Для полей вещественных типов возвращается System.Double.

Для получения значений Аксы вполне работал способ:
X++:
System.Object   sysObj;
anytype ret;

sysObj = dataReader.get_Item(_fieldNum);
ret = ClrInterop::getAnyTypeForObject(_sysObj);
Как только понадобилось запустить это в пакете (то есть с CIL) именно для System.Double getAnyTypeForObject не работает - возвращает не значение базового типа real, а объект System.Double.

Естественно, понять что именно не работает удалось только в отладчике VS.

Понятно, что конкретный случай получилось обойти путем написания кучи кода:
X++:
protected anytype systemObjectToAnyType(System.Object _sysObj)
{
    anytype         ret;
    System.Type     retType;
    str             retTypeName;
    str             valueStr;
    boolean         useStdConvert;
    ;

    useStdConvert   = true;
    if (xSession::isCLRSession())
    {
        retType = _sysObj.GetType();
        retTypeName = retType.get_Name();
        if (retTypeName == 'double')
        {
            // Весёлости взаимодействия System.Double и real X++.
            // Стандарт не конвертирует, приходится выполнять закат солнца вручную.
            valueStr    = _sysObj.ToString();
            ret = str2num_RU(valueStr);
            useStdConvert   = false;
        }
    }

    if (useStdConvert)
    {
        // BP deviation documented
        ret = ClrInterop::getAnyTypeForObject(_sysObj);
    }

    return ret;
}
Может я тут что-то перемудрил и есть более простые варианты?
В стандартном приложении нашел много кода, когда наоборот - имеем переменную real X++ и нужно передать туда, где требуется System.Double - даже в global метод для этого создали. Видимо сталкивались с проблемами преобразования в CIL real->System.Double. А вот ситуации когда имеем System.Double и нужно просто присвоить её real X++ не обнаружил.
Старый 11.11.2023, 18:47   #2  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1293 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Вообще, даже в методе nullValueBaseType класса Global пришлось делать "затычку":
Объявить переменную:
RealBase realValue;

Далее:
X++:
        case Types::Real:
            // FIX klimov 10.11.2023 -->
            if (xSession::isCLRSession())
            {
                // Весёлый CIL.
                // Если просто вернуть 0.0 в anytype, то в CIL вернется объект System.Double.
                // А если не вызывать Convert, то будет не 0, а 0.___значение (типа 0.0000000000578).
                realValue   = System.Convert::ToDouble(0.0);
                return realValue;
            }
            else
            {
            // FIX klimov 10.11.2023 <--
                return 0.0;
            }
Иначе в CIL простой метод добавления поля в Struct с пустым значением:
X++:
recordStruct.add(fieldName, nullValueBaseType(fieldType));
для Types Real создавал поле с типом Class.
Старый 11.11.2023, 22:05   #3  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,940 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Встречал похожие приколы в других местах.
Были проблемы для anytype для неинициализированных значений.
Может у вас там null приходил ?

а если просто присвоить значение ?
Например
real retReal;

retReal = _sysObj;
// или так
retReal = _sysObj as System.Double;
// или так
retReal = any2real(_sysObj as System.Double);

ret = retReal;
За это сообщение автора поблагодарили: Raven Melancholic (2).
Старый 12.11.2023, 14:23   #4  
SRF is offline
SRF
Участник
MCBMSS
Axapta Retail User
 
375 / 562 (19) +++++++
Регистрация: 08.08.2007
Записей в блоге: 1
Насколько я понимаю real соответствует типу System.Decimal, а не System.Double (https://learn.microsoft.com/en-us/dy...eveloper/reals).

Думается, что если использовать Decimal, то должно работать нормально (т.е. возвращать real):
X++:
if (sysObj is System.Double)
{
   ret = ClrInterop::getAnyTypeForObject(System.Convert::ToDecimal(sysObj));
}
Согласен с Logger, any2real тоже выглядит вполне рабочим вариантом.

Интересно почему он 0.0 в nullValueBaseType конвертирует в System.Double, а не в System.Decimal.

А если вот такой код будет, то он также будет возвращать System.Double ?
X++:
if (xSession::isCLRSession())
{
    return realValue;
}
__________________
Sergey Nefedov
За это сообщение автора поблагодарили: Logger (5), Raven Melancholic (2).
Старый 12.11.2023, 16:22   #5  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1293 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Цитата:
Думается, что если использовать Decimal, то должно работать нормально (т.е. возвращать real).
Да, работает, но маленький, но важный момент - вместо 1,034 возвращает 1,034000009564.

Цитата:
Интересно почему он 0.0 в nullValueBaseType конвертирует в System.Double, а не в System.Decimal.
Не знаю, но отладчик VS показывает именно double.

Цитата:
Согласен с Logger, any2real тоже выглядит вполне рабочим вариантом
.
Может и рабочий, но получаем ошибку (ловится в VS) "Неправильный тип аргумента функции преобразования."
Старый 30.11.2023, 14:10   #6  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,940 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от Raven Melancholic Посмотреть сообщение
Да, работает, но маленький, но важный момент - вместо 1,034 возвращает 1,034000009564.
Был похожий прикол
AIF + Web Services + JSON

в 2009-й была проблема, в 12-ке - нет. Возможно как-то завязано на версию .Net
За это сообщение автора поблагодарили: SRF (2).
Старый 12.11.2023, 16:26   #7  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1293 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Ну и Decimal - Double в CIL уже народ сталкивался:
Поле "XXX" (= -0,000) может содержать только положительные числа
Старый 15.11.2023, 00:38   #8  
Товарищ ♂uatr is offline
Товарищ ♂uatr
Участник
Аватар для Товарищ ♂uatr
MCBMSS
 
299 / 866 (29) +++++++
Регистрация: 23.10.2012
Добрый вечер.
Тоже пользуемся данной библиотекой, но используем функцию getValue. Суть одна и та же, она возвращает тот же самый Object.
Однако, в отличии от использования унифицированной функции работающей с anyType (который, между прочим, есть тот самый System.Object) выделили набор функций для приведения результата к типам Аксапты.
Logger выше писал, просто разовью мысль:
Для real своя функция valueToReal, для string - valueToString и тд.
Здесь стоит акцентировать внимание на том, что в функции необходимо использовать результирующую переменную, в ином случае вернется всё тот же System.Object.
Т.е.:
X++:
public string valueToString(... _reader, _columnIndex)
{
   string ret;
   ;

   if (_reader.IsDBNull(_columnIndex))
   {
      ret = _reader.GetValue(_columnIndex);
   }

   return ret;
}
Но ни в коем случае не:
X++:
public string valueToString(... _reader, _columnIndex)
{
   return _reader.IsDBNull(_columnIndex) ? "" : _reader.GetValue(_columnIndex);
}

Последний раз редактировалось Товарищ ♂uatr; 15.11.2023 в 00:41.
За это сообщение автора поблагодарили: Raven Melancholic (10).
Старый 15.11.2023, 10:23   #9  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1293 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Отлично!
А как вы определяете какую из функций вызвать в случае, если не знаете заранее тип?

Что-то мне использование System.Type с получением наименования:
X++:
retType = _sysObj.GetType();
retTypeName = retType.get_Name();
if (retTypeName == '...')
не сильно нравится. Может есть что-то более "технологичное"?
Старый 15.11.2023, 12:44   #10  
Товарищ ♂uatr is offline
Товарищ ♂uatr
Участник
Аватар для Товарищ ♂uatr
MCBMSS
 
299 / 866 (29) +++++++
Регистрация: 23.10.2012
В рамках существующих интеграций знаем типы данных...
...придется фантазировать:
Особой вариативности не вижу, потому что за капотам используется внутренний объект и доступа к нему из вне нет (System.Data.SqlClient._SqlMetaData).
Остается использовать то, что есть - System.Type.
Его можно получить, как минимум 2 способами:
- из reader'a (функция GetFieldType);
- из самого объекта, что и делаете.
С точки зрения эстетики Guid куда приятнее - для объекта типа System.Type функция get_GUID. GUID'ы типов данных можно обернуть в макросы.
Встречал реализацию, где сравнивается сам System.Type, но то с чем он сравнивается нужно держать в памяти.
Старый 15.11.2023, 15:04   #11  
Товарищ ♂uatr is offline
Товарищ ♂uatr
Участник
Аватар для Товарищ ♂uatr
MCBMSS
 
299 / 866 (29) +++++++
Регистрация: 23.10.2012
Гугл подсказал, что данный способ является оптимальным, C#:
X++:
System.Type type = reader.GetFieldType(c);

switch (Type.GetTypeCode(type))
{
    case TypeCode.DateTime:
        break;
    case TypeCode.String:
        break;
    default: break;
}
За это сообщение автора поблагодарили: Logger (5).
Старый 01.12.2023, 09:24   #12  
Raven Melancholic is offline
Raven Melancholic
Участник
Аватар для Raven Melancholic
Самостоятельные клиенты AX
Лучший по профессии 2015
 
2,164 / 1293 (48) ++++++++
Регистрация: 21.03.2005
Адрес: Москва-Петушки
Да, это видел.
А так же Поле "XXX" (= -0,000) может содержать только положительные числа

Правда по теме я ступил.
Сам же три года назад писал разные методы для разных типов NET при обмене с внешней базой. Просто они в другом классе-обертке.
Старый 01.12.2023, 10:24   #13  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,940 / 3229 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Так какое же решение выбрали?
 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
fed: Net requirements update in MRP Module and Working Set of MRP Blog bot DAX Blogs 14 08.05.2012 13:09
AX.NET: интеграция .NET-приложений с Аксаптой и (будущие) возможности облачных вычислений gl00mie DAX: Программирование 2 23.04.2010 00:47
Dynamics AX: Map Object Sorting - a real issue Blog bot DAX Blogs 7 15.10.2008 12:02
Dynamics AX: .Net BC, C# - Working with AxaptaRecord Object Blog bot DAX Blogs 0 20.06.2007 23:13
Dynamics AX: .Net BC Custom App, Part III - Container Object Blog bot DAX Blogs 0 06.06.2007 22:11

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

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

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