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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 19.06.2009, 18:40   #1  
petergunn is offline
petergunn
Участник
 
118 / 274 (10) ++++++
Регистрация: 30.08.2005
Адрес: Tyumen
! DictEnum.index2XXXX() & DictEnum.value2XXXX()
Dynamics Ax 4.0 SP2 (application version: 4.0.2501.347), Dynamics Ax2009 (application version: 5.0.1001.176)

После темы Баг в InventItemType? из 'спортивного интереса' решил посмотреть на сколько часто в коде Ax встречаются ситуации когда разработчики использовали 'индекс' (порядковый номер) элемента перечисления в качестве его значения, либо наоборот - трактовали значение как порядковый номер элемента перечисления.

По перекрестным ссылкам выборочно посмотрел где(как) используются методы классов (Sys-)DictEnum.index2Name(), (Sys-)DictEnum.index2Symbol(), (Sys-)DictEnum.value2Name(), (Sys-)DictEnum.value2Symbol() и методы SysDictEnum: firstValue(), lastValue(), nextValue(). В результате обнаружилось достаточное количество мест, код которых вызывает вопросы и претендует на рефакторинг. Приведу некоторые примеры кода на анализ которого потратил немного времени.

Forms\SysRecordInfo\Methods\buildInsertScript():
X++:
void buildInsertScript()
{
    ...
    boolean setFieldValue(fieldId _fieldId)
    {
        ...
        switch (dictFieldTemp.baseType())
        {
            ...
            case Types::Enum :
                dictEnum = new DictEnum(dictFieldTemp.enumId());
                fieldValue = dictEnum.name() + '::' + dictEnum.index2Symbol(common.(_fieldId));
                break;
            ...
        }
        ...
    }
    ...
}
в поле common.(_fieldId) - значение перечисления, логичнее ожидать в этом коде использование dictEnum.value2Symbol:
X++:
    fieldValue = dictEnum.name() + '::' + dictEnum.value2Symbol(common.(_fieldId));
Увидеть результаты исходного варианта кода можно к примеру в справочнике номенклатуры (для российской функциональности): выбираем строку номенклатуры с типом 'Основные средства' (ItemType=Asset_RU(100)), из контекстного меню выбираем Record Info ('Паспорт записи') и жмем кнопку Script ('Сценарий'). Получаем результат вида:
Цитата:
InventTable.ItemGroupId = "Электротехника";
InventTable.ItemId = "Эле_000085";
InventTable.ItemName = "Перфоратор с пылесосом, Макита";
InventTable.ItemType = ItemType::;

Classes\CCPivotTable\addEnums():
X++:
void addEnums(SysDictField sysDictField)
{
    ...
    for (n=0; n<dictEnum.values(); n++)
    {
        OLAPEnums.EnumValue = n;
        OLAPEnums.EnumText = dictEnum.value2Name(n);
        OLAPEnums.EnumName = dictEnum.name();
        OLAPEnums.Language = languageId;

        OLAPEnums.insert();
    }
    ...
}
ожидалось:
X++:
    OLAPEnums.EnumText = dictEnum.index2Name(n);

Classes\SysLookup\enumLabel2Id():
X++:
// return -1 when enum label does not exsist.
public client server static int enumLabel2Id(enumId _enumId, LabelType _labelType)
{
    DictEnum dictEnum = new DictEnum(_enumId);
    int i;
    ;
    for (i=0; i < dictEnum.values(); i++)
    {
        if (!strcmp(strltrim(dictEnum.value2Label(i)), strltrim(_labelType)))
        {
            return i;
        }
    }

    for (i=0; i < dictEnum.values(); i++)
    {
        if (!strcmp(strltrim(dictEnum.value2Name(i)), strltrim(_labelType)))
        {
            return i;
        }
    }

    for (i=0; i < dictEnum.values(); i++)
    {
        if (!strcmp(strltrim(int2str(i)), strltrim(_labelType)))
        {
            return i;
        }
    }

    return -1;
}
По коду делаем вывод что метод возвращает порядковый номер в перечислении при совпадении текста поиска.
Результат метода используется в SysLookup::lookupTableFixedRelation():
X++:
private static RelationName lookupTableFixedRelation(tableId _tableId, Common _common, Set _fixedRelationSet)
{
    ...
    if (relationField.enumId())
    {
        ...
        if (tmpSysQuery &&
            dictRelation.lineExternTableValue(j) == SysLookup::enumLabel2Id(relationField.enumId(), tmpSysQuery.RangeValue))
        ...
    } 
    ...
}
т.е. числовое значение перечисления в фиксированной связи между таблицами dictRelation.lineExternTableValue(j) сравнивается c порядковым номером элемента перечисления SysLookup::enumLabel2Id() найденного по тексту? Тогда для перечислений с 'дырками' в значениях элементов метод будет работать не совсем корректно.

Для тестирования набросал небольшой job:
X++:
static void jbSysLookupCheck(Args _args)
{
    void checkSysLookup_EnumLabel2Id( ItemType _itemType )
    {
        SysDictEnum sysDictEnum = new SysDictEnum( enumNum( ItemType ) ) ;
        LabelType   labelType   ;
        ;

        setPrefix( strfmt( "%1: %2 (%3)", sysDictEnum.name(), _itemType, any2int( _itemType ) ) ) ;

        labelType = sysDictEnum.value2Label( _itemType ) ;
        info( strfmt( "by label: %1 (%2)", labelType, SysLookup::enumLabel2Id( sysDictEnum.id(), labelType ) ) ) ;

        labelType = sysDictEnum.value2Name( _itemType ) ;
        info( strfmt( "by name: %1 (%2)", labelType, SysLookup::enumLabel2Id( sysDictEnum.id(), labelType ) ) ) ;

        labelType = strfmt( "%1", any2int( _itemType ) ) ;
        info( strfmt( "by value: %1 (%2)", labelType, SysLookup::enumLabel2Id( sysDictEnum.id(), labelType ) ) ) ;
    }
    ;

    info( "SysLookup::enumLabel2Id()" ) ;
    checkSysLookup_EnumLabel2Id( ItemType::Service ) ;
    checkSysLookup_EnumLabel2Id( ItemType::Asset_RU ) ;
}
результат выполнения:
Цитата:
SysLookup::enumLabel2Id()
-ItemType: Услуга (2)
--by label: Услуга (2)
--by name: Услуга (2)
--by value: 2 (2)
-ItemType: Основные средства (100)
--by label: Основные средства (-1)
--by name: Основные средства (-1)
--by value: 100 (-1)
Возможно что метод должен был выглядеть примерно так (возвращать значение перечисления по искомому тексту?):
X++:
// return -1 when enum label does not exsist.
public client server static int enumLabel2Id(enumId _enumId, LabelType _labelType)
{
    DictEnum dictEnum = new DictEnum(_enumId);
    int i;
    ;
    for (i=0; i < dictEnum.values(); i++)
    {
        if (!strcmp(strltrim(dictEnum.index2Label(i)), strltrim(_labelType)))
        {
            return dictEnum.index2Value(i);
        }
    }

    for (i=0; i < dictEnum.values(); i++)
    {
        if (!strcmp(strltrim(dictEnum.index2Name(i)), strltrim(_labelType)))
        {
            return dictEnum.index2Value(i);
        }
    }

    for (i=0; i < dictEnum.values(); i++)
    {
        if (!strcmp(strltrim(int2str(dictEnum.index2Value(i))), strltrim(_labelType)))
        {
            return dictEnum.index2Value(i);
        }
    }

    return -1;
}

Classes\smmSourceType\preDefinedType() (AX2009):
X++:
public static boolean preDefinedType(str _sourceType)
{
    SysDictEnum sysDictEnum = new SysDictEnum(enumnum(SmmSourceTypeList));
    int i;
    ;

    for (i = sysDictEnum.firstValue(); i <= sysDictEnum.lastValue(); i = sysDictEnum.nextValue(i))
    {
        if (sysDictEnum.index2Value(i) != SmmSourceTypeList::UserDefined &&
            sysDictEnum.index2Name(i) == _sourceType)
        {
            return true;
        }
        if (i == sysDictEnum.lastValue())
        {
            break;
        }
    }
    return false;
}
обратная ситуация: методы sysDictEnum.firstValue(), sysDictEnum.nextValue() возвращают числовое значение перечисления для переменной i, однако далее по коду значение i трактуется как порядковый номер и используются методы index2Value(i) и index2Name(i).

imho, ожидалась проверка значения:
X++:
    if ( i != SmmSourceTypeList::UserDefined &&
         sysDictEnum.value2Name(i) == _sourceType)
Похожая проверка значения перечисления в этом же классе есть на методе smmSourceType.createDefaultData().

Для тестирования добавил в перечисление SmmSourceTypeList элемент CheckCode_Example_100, с меткой 'Example' и значением 100 и запустил job:
X++:
static void jbSmmSourceTypeCheck(Args _args)
{
    void checkSmmSourceType( SmmSourceTypeList _smmSourceTypeList)
    {
        ;
        info( strfmt( "%1 (%2) - %3", _smmSourceTypeList, any2int( _smmSourceTypeList ), SmmSourceType::preDefinedType( strfmt( '%1', _smmSourceTypeList ) ) ) ) ;
    }
    ;

    setPrefix( "SmmSourceType::preDefinedType()" ) ;
    checkSmmSourceType( SmmSourceTypeList::Employee ) ;
    checkSmmSourceType( SmmSourceTypeList::CheckCode_Example_100 ) ;
}
Результат:
Цитата:
SmmSourceType:: preDefinedType()
- Сотрудник (2) - true
- Example (100) - false

Пример из кода локальной функциональности, Classes\InventJournalPrintForm_RU.run():
X++:
private void run()
{
    ...
    DictEnum        dictEnum = new DictEnum(enumnum(InventJournalReportType_RU));
    ...
    InventJournalReport_RU      report;
    ...

    while (it.more())
    {
        ++gridCnt;
        report = it.value();
        dsName = dictEnum.index2Symbol(report.reportType());
        ...
    }
    ...
    while (it.more())
    {
        report = it.value();
        dsName = dictEnum.index2Symbol(report.reportType());
        ...
    }
    ...
}
InventJournalReport_RU.reportType() возвращает значение перечисления InventJournalReportType_RU, а в коде трактуется как 'index' : dsName = dictEnum.index2Symbol(report.reportType());
Ожидалось
X++:
        dsName = dictEnum.value2Symbol(report.reportType());
Это лишь некоторые примеры из числа найденных, описывать все думаю нет необходимости, принцип поиска достаточно прост.

P.S. Надеюсь члены группы Microsoft Dynamics (если они просматривают форум) обратят внимание на этот топик, по возможности прокоментируют сделанные выводы и инициируют рефакторинг прикладного кода использующего методы DictEnum.value2XXXX() и DictEnum.index2XXXX() на предмет устранения подобных накладок (скрытых багов).
За это сообщение автора поблагодарили: mazzy (5), jeky (1).
Старый 19.06.2009, 19:47   #2  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Рефакторинг не обещаю, но некорректные использования пофиксят, думаю.
За это сообщение автора поблагодарили: Logger (5).
Старый 20.06.2009, 15:36   #3  
petergunn is offline
petergunn
Участник
 
118 / 274 (10) ++++++
Регистрация: 30.08.2005
Адрес: Tyumen
new DictEnum()
Dynamics Ax 4.0 SP2 (application version: 4.0.2501.347), Dynamics Ax2009 (application version: 5.0.1001.176)

Еще немного о DictEnum: использование числовых идентификаторов вместо enumNum() - тут Best Practices не помешал бы
  • 102 - enumNum(ReqRefType)
  • 112 - enumNum(ItemCalcType)
  • 118 - enumNum(ItemType)
\Classes\ReqTransFormExplosion\treeImageExplain()
X++:
void treeImageExplain()
{
    DictEnum        dictEnum = new DictEnum(102);
    int             counter;
    int             imageNo;
    ;
    ctrlImageExplain.deleteAll();
    ctrlImageExplain.visible(tmpReqExplosionTree.SettingsDisplayImageExplain);

    if (!tmpReqExplosionTree.SettingsDisplayImageExplain)
        return;

    while (counter <  40 /*enumcnt(ReqRefType)*/)
    {
        counter++;
        imageNo = this.displayImageReqRefType(counter);
        if (imageNo)
        {
            //BP Deviation documented
            ctrlImageExplain.addItem(new FormListItem(dictEnum.value2Name(counter),imageNo));
        }
    }

    imageNo = this.displayImageReqRefType(ReqRefType::ItemPlannedOrder,true);
    if (imageNo)
    {
        //BP Deviation documented
        ctrlImageExplain.addItem(new FormListItem("@SYS9646",imageNo));
    }
}
\Forms\BOMCalcTrans\Designs\Design\[Tab:Tab]\[TabPage:Overview]\[Grid:Grid]\Window:Type\Methods\toolTip
X++:
str toolTip()
{
    DictEnum    dictEnum = new DictEnum(112);
    ...
}
\Forms\BOMChangeLine\Designs\Design\[Tab:Tab]\[TabPage:Overview]\[Grid:GridBOM]\Window:ItemTypeIcon\Methods\toolTip
\Forms\BOMConsistOf\Designs\Design\[Group:GroupBOM]\[Tab:Tab]\[TabPage:Overview]\[Grid:GridBOM]\Window:ItemTypeIcon\Methods\toolTip
X++:
str toolTip()
{
    DictEnum    dictEnum = new DictEnum(118);
    ...
}
\Forms\ProdCalcTrans\Designs\Design\[Tab:Tab]\[TabPage:OverviewCosting]\[Grid:CostingGrid]\Window:TypeCostingGrid\Methods\toolTip
\Forms\ProdCalcTrans\Designs\Design\[Tab:Tab]\[TabPage:OverviewEstimation]\[Grid:EstimationGrid]\Window:TypeEstimationGrid\Methods\toolTip
X++:
str toolTip()
{
    DictEnum    dictEnum = new DictEnum(112);
    ...
}
За это сообщение автора поблагодарили: ZVV (5).
Старый 23.06.2009, 14:24   #4  
in.dc is offline
in.dc
Участник
 
29 / 53 (2) ++++
Регистрация: 09.04.2009
enumcnt() & dictEnum.value2Name() & InventItemType::construct()
Dynamics Ax 4.0 SP2 EE
Добавлю своих '5 копеек' до кучи, где 'индекс' трактуется как значение перечисления:
  • \Classes\BOMDesignerCtrl\controlImageExplain
  • \Classes\SMABOMDesignerCtrl\controlImageExplain
  • \Forms\ConfigHierarchy\Methods\initImageExplain
X++:
    DictEnum        dictEnum = new DictEnum(enumnum(ItemType));
    ...
    for (i=enumcnt(ItemType);i>0;i--)
    {
        inventItemType = InventItemType::construct(i-1);
        if (inventItemType)
        {
            imageNo = inventItemType.imageRessNo();
            if (imageNo)
            {
                //BP Deviation documented
                imageExplain.addItem(new FormListItem(dictEnum.value2Name(i-1),imageList.image(imageNo)));
            }
        }
    }
__________________
Dynamics AX 4.0 SP2
Теги
ax2009, ax4.0, dictenum, dictionary, баг

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
AX UK: Dynamics AX 2009 Sales & Presales Training Blog bot DAX Blogs 0 11.03.2008 07:09
Как в range на одно и тоже контейнерное поле поставить условие: "исключ." && like Pustik DAX: Программирование 15 03.07.2004 11:54
Ассоциативность операции && alexbn DAX: Программирование 9 10.06.2004 17:18

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

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

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