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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 02.09.2011, 00:06   #1  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Вспомогательные классы проверки условий и утверждений
С одной стороны, хочется
  • в коде по возможности следовать контрактному программированию, чтобы ошибки не распространялись по коду и данным, а выявлялись как можно раньше;
  • выводить детализированные, четкие и понятные предупреждения и сообщения об ошибках в ходе проверки пред/постусловий и проч., чтобы и пользователи, и специалисты поддержки понимали, почему код "сломался" и/или не делает то, что ожидается, и не задавали лишних вопросов (сообщения вида "класс вызван с неверными параметрами" без дополнительных пояснений - это ни о чем);
  • следовать принципу DRY (don't repeat yourself) и не дублировать логику проверки только ради вывода сообщений об ошибках (как при этом реализовать дублирующие друг друга isXX и checkXX-методы?);
  • наконец, писать ясный, лаконичный и легко модифицируемый код.
А с другой стороны, писать в тысячный раз if (!this.Field) ret = checkFailed(strfmt("поле %1 должно быть заполнено" ... и весь сопутствующий код с ветвлениями, подчас, нет уже просто никаких сил. Из этих соображений возникла идея сделать сперва несколько методов в Global, а потом - пару специализированных классов для проверки наиболее часто встречающихся условий и утверждений, чтобы, с одной стороны, код был более компактным, понятным и надежным, а с другой, чтобы выводимые сообщения об ошибках и предупреждения были максимально информативны и при этом отключаемыми (тогда checkXX-метод легко превратится в isXX за счет дополнительного параметра). То, что получилось, - во вложении, это два класса DEV_Check и DEV_Assert, а также несколько вспомогательных модификаций общего назначения, плюс класс DEV_Desc4, задуманный изначально как универсальный "описатель" объектов вообще, но пока умеющий описывать лишь записи различных таблиц.
С ним идея была в том, чтобы в сообщениях на запись практически любой таблицы можно было ссылаться с помощью одного placeholder'а (%1), но чтобы описание при этом получалось необходимым и достаточным для идентификации записи, о которой идет речь: если это SalesTable, то чтобы был указан SalesId, если CustTable/VendTable - чтобы там был AccountNum, если CustTrans/VendTrans - чтобы обязательно были Voucher и TransDate и т.п., ну и чтобы везде был RecId, как в отладчике. И при всем при этом чтобы вызывающий код не заморачивался, описание записи какой именно таблицы он выводит.
Писать обо всем этом можно много, лучше приведу несколько примеров.

Проверки утверждений
X++:
DEV_Assert::hasTableAccess( tableBuffer.TableId, AccessType::Delete );
delete_from tableBuffer
    where // ...

salesOriginId = DEV_Assert::returnedParmTableFieldIsNotEmpty( SalesParameters::find(), fieldnum(SalesParameters, SalesOriginId) );
// если поле не заполнено, вылетит ошибка с SysInfoAction для открытия формы, связанной с параметрической таблицей

public void modifiedArrayFieldElement(ArrayIdx _idx)
{;
    DEV_Assert::arrayIdxIsValid(_idx, dimof(this.ArrayField));
    // на некорректном индексе вылетит исключение
Метод возвращает признак корректности параметров доставки почты
X++:
protected boolean validateTransportParms()
{
    boolean ret =           DEV_Check::tableFieldNotEmpty( sysEmailParms, fieldnum(SysEmailParameters, SMTPRelayServerName) )
                &&          DEV_Check::tableFieldValueComparesTo( sysEmailParms, fieldnum(SysEmailParameters, SMTPPortNumber), DEV_ComparisionOp::More, 0 )
                &&  (      !mustAuthenticate
                    ||  (   DEV_Check::tableFieldNotEmpty( sysEmailParms, fieldnum(SysEmailParameters, SMTPUserName) )
                        &&  DEV_Check::parameterNotEmpty( smtpPassword, fieldpname(SysEmailSMTPPassword, Password) )
                        )
                    )
                            ;
    return  ret;
}
Класс или отчет проверяет, что он корректно вызван
X++:
DEV_Assert::methodIsCalledCorrectly(
        // если одно из условий окажется не выполненным, метод methodIsCalledCorrectly()
        // выведет в ошибке путь к вызвавшему его методу, взятый из стека вызовов
        DEV_Check::tableBufferInArgsIsSupportedAndNotEmpty( _args, tablenum(SalesTable) )
    &&  DEV_Check::argsParmEnumTypeIs( _args, enumnum(NoYes) )
    &&  DEV_Check::objectIs( _args.caller(), classnum(SalesFormLetter)
);
Ну и более гхм... навороченный случай - из генератора скриптов конвертации базы под AX 2009
X++:
// устанавливает значения выражений для заполнения исходными данными конечного поля типа UtcDateTime и,
// опционально, сопутствующего поля "TZID", по ходу выполняя дополнительные проверки и выводя предупреждения
public void setSourceClauses4DestUtcDateTimeField(
    DEV_SysDestSqlDictionary    _destDateTimeSqlDict,
    str                         _srcDateTimeSqlClause,
    fieldId                     _srcDateFieldId = 0,
    fieldId                     _srcTimeFieldId = 0,
    DEV_SysDestSqlDictionary    _destTzIdSqlDict = null,
    str                         _srcTzIdSqlClause = ''
    )
{
    fieldId                     srcDateFieldExtId;
    fieldId                     srcTimeFieldExtId;

    setprefix( strfmt( @"Установка выражения для заполнения поля %1 (TZID %2)",
                       DEV_SysDbMigrationUtil::desc4Field( _destDateTimeSqlDict ), DEV_SysDbMigrationUtil::desc4Field( _destTzIdSqlDict ) ) );
    DEV_Assert::methodIsCalledCorrectly(
                // здесь не дублируем проверки _destDateTimeSqlDict из setSourceClause4DestinationFieldInternal()
                    DEV_Check::tableFieldValue( _destDateTimeSqlDict, fieldnum(DEV_SysDestSqlDictionary, fieldType), #TypesUtcDateTime )
                // если не указано исходное поле с датой, то и исходное поле со временем указано быть не должно
        &&  (   (   _srcDateFieldId == 0
                &&  DEV_Check::parameterValue( _srcTimeFieldId, 0, identifierstr(_srcTimeFieldId) )
                )
                // если указано исходное поле с датой, то исходное поле со временем должно быть отличным от него
            ||  (   _srcDateFieldId != 0
                &&  DEV_Check::parameterValueNot( _srcTimeFieldId, _srcDateFieldId, identifierstr(_srcTimeFieldId) )
                )
            )   // для системных полей createdDateTime/modifiedDateTime не должно быть поля "TZID"
        &&  (   (   isSysId( _destDateTimeSqlDict.fieldId )
                &&  DEV_Check::tableFieldValue( _destTzIdSqlDict, fieldnum(DEV_SysDestSqlDictionary, fieldId), 0 )
                &&  DEV_Check::parameterValue( _srcTzIdSqlClause, '', identifierstr(_srcTzIdSqlClause) )
                )
                // для несистемных полей типа UtcDateTime поле "TZID" должно быть обязательно указано, причем для него есть ряд доп. требований
            ||  (  !isSysId( _destDateTimeSqlDict.fieldId )
                &&  DEV_Check::tableBufferNotEmpty( _destTzIdSqlDict )
                &&  DEV_Check::tableFieldValue( _destTzIdSqlDict, fieldnum(DEV_SysDestSqlDictionary, tabId),        _destDateTimeSqlDict.tabId )
                &&  DEV_Check::tableFieldValue( _destTzIdSqlDict, fieldnum(DEV_SysDestSqlDictionary, fieldId),      _destDateTimeSqlDict.fieldId )
                &&  DEV_Check::tableFieldValue( _destTzIdSqlDict, fieldnum(DEV_SysDestSqlDictionary, fieldType),    Types::Integer )
                &&  DEV_Check::tableFieldValueComparesTo( _destTzIdSqlDict, fieldnum(DEV_SysDestSqlDictionary, array), DEV_ComparisionOp::More, _destDateTimeSqlDict.array )
                &&  DEV_Check::parameterValueNot( _srcTzIdSqlClause, _srcDateTimeSqlClause, identifierstr(_srcTzIdSqlClause) )
                )
            )
        );
    this.setSourceClause4DestinationFieldInternal( _destDateTimeSqlDict, _srcDateTimeSqlClause, true );
    this.markDestinationUtcDateTimeFieldAsFilled( _destDateTimeSqlDict );
    this.markSourceFieldIdAsMigrated( _srcDateFieldId );
    this.markSourceFieldIdAsMigrated( _srcTimeFieldId );
    if (_destTzIdSqlDict)
    {
        this.setSourceClause4DestinationFieldInternal( _destTzIdSqlDict, _srcTzIdSqlClause );
    }
}
Вложения
Тип файла: rar DEV_Utils.rar (13.8 Кб, 310 просмотров)

Последний раз редактировалось gl00mie; 02.09.2011 в 00:21. Причина: typo...
За это сообщение автора поблагодарили: mazzy (2), AlGol (1), Logger (10), S.Kuskov (3).
Старый 02.09.2011, 08:44   #2  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,427 / 1771 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от gl00mie Посмотреть сообщение
выводить детализированные, четкие и понятные предупреждения и сообщения об ошибках в ходе проверки пред/постусловий и проч., чтобы и пользователи, и специалисты поддержки понимали, почему код "сломался" и/или не делает то, что ожидается, и не задавали лишних вопросов (сообщения вида "класс вызван с неверными параметрами" без дополнительных пояснений - это ни о чем);
Вот с этим пунктом не совсем согласен (а он на самом деле является ключевым). В смысле не согласен с тем что представленное средство решает эту проблему. Скорее даже наоборот.

Чем принципиально отличается детализированные сообщения от недетализированных? Тем что вместе с ошибкой "класс вызван с неверными параметрами" будет выведена ещё и трасировка стека? Или в сообщении "поле должно быть заполненно" будет полностью расшифрованы названия таблиц и обязательных полей? Нет пользователям не нужны такие детализированные сообщения, пользователям нужны сообщения на естественном языке, на языке бухгалтера, экономиста, кладовщика и т.д и т.п. Вот программистам нужны детализированные сообщения. Так им проще локализовать ошибку.

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

Последний раз редактировалось S.Kuskov; 02.09.2011 в 08:48.
За это сообщение автора поблагодарили: mazzy (2).
Старый 02.09.2011, 10:08   #3  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Чем принципиально отличается детализированные сообщения от недетализированных? Тем что вместе с ошибкой "класс вызван с неверными параметрами" будет выведена ещё и трасировка стека? Или в сообщении "поле должно быть заполненно" будет полностью расшифрованы названия таблиц и обязательных полей?
Нет, сообщения будут вполне обычными: поле такое-то должно быть заполнено, поле такое-то НЕ должно быть заполнено, значение поля такого-то должно быть в диапазоне от и до, значение поля должно быть больше/меньше/не равно такому-то значению и т.п. Идея в том, чтобы сделать "говорящими" по возможности все логические выражения, не перегружая при этом код, что в сочетании с грамотным использованием setprefix() подчас просто творит чудеса Без подобных средств модификации зачастую пишутся так, что проверяется с десяток самых разных условий, делается пяток ветвлений в коде, и на выходе пользователь получает лишь скупое "обновление было отменено".
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
пользователям не нужны такие детализированные сообщения, пользователям нужны сообщения на естественном языке, на языке бухгалтера, экономиста, кладовщика и т.д и т.п.
У вас наверняка в Аксапту уже встроен искусственный интеллект? Пользователи могут не подозревать о том, какой сложности функционал отрабатывает в ответ на их действия, именно поэтому в т.ч. им нужны детализированные сообщения об ошибках, в которых передается контекст происходящего и объясняется, какие проверки привели к тем или иным решениям.
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Вот программистам нужны детализированные сообщения. Так им проще локализовать ошибку.
"Если врач сыт, то и больному легче..." (с) х/ф "Формула любви"
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Есть конечно такие области системы (базовые классы, таблицы или универсальные обработки), в которых находится общая логика, где уже при всём желании невозможно конкретизировать выполняемую в данный момент задачу.
Да все возможно - setprefix() рулит! Просто нужно контекст передавать в сообщениях об ошибках, тогда они станут намного понятнее и доходчивее.
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
Задача же клиентского кода работающего с конкретной задачей пропустить минимум ошибок мимо себя в такие базовые области. Т.е. Нужно стремиться ловить ошибки как можно ближе к месту их возникновения, тогда будет проще описывать их на естественном языке.
Непонятно, что тут понимается под клиентским кодом. validateField() на таблице - это клиентский код?..
Старый 02.09.2011, 10:20   #4  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от gl00mie Посмотреть сообщение
пару специализированных классов для проверки наиболее часто встречающихся условий и утверждений
В этом и беда универсальных методов - часто встречающиеся они обрабатывают... А вот с отсальными приходится мучаться.

Цитата:
Сообщение от gl00mie Посмотреть сообщение
С ним идея была в том, чтобы в сообщениях на запись практически любой таблицы можно было ссылаться с помощью одного placeholder'а (%1), но чтобы описание при этом получалось необходимым и достаточным для идентификации записи, о которой идет речь: если это SalesTable, то чтобы был указан SalesId, если CustTable/VendTable - чтобы там был AccountNum, если CustTrans/VendTrans - чтобы обязательно были Voucher и TransDate и т.п., ну и чтобы везде был RecId, как в отладчике.
мне кажется, что вместо такой хотелки стоит рассмотреть следующий стандартный подход.
  1. ВСЕГДА надо писать setprefix("информация о том, что обрабатываем в данный момент - информация об источнике")
  2. сообщения об ошибках должны содержать только информацию об ошибке, но не должны содержать информацию об источнике ошибки
  3. (опционально) вместо вывода recid всегда юзать SysInfoAction

при таком подходе информация об источнике возникновения выводится там, где она реально имеется. при таком подходе эту информацию не нужно спускать на нижние уровни.

если юзать SysInfoAction то пользователю не нужно будет перевбивать в поиск recId - достаточно просто нажать на кнопку в инфологе.

беда в том, что люди не юзают даже стандартные подходы. Даже в Майкрософте.

=================
краткий совет - если не хватает информации об источнике ошибки, то всего-лишь добавьте setprefix в циклы верхнего уровня.
__________________
полезное на axForum, github, vk, coub.
Старый 02.09.2011, 10:54   #5  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Цитата:
Сообщение от mazzy Посмотреть сообщение
В этом и беда универсальных методов - часто встречающиеся они обрабатывают... А вот с отсальными приходится мучаться.
"No silver bullet". С другой стороны, если есть какой-то способ избавиться от 80% рутины в выводе сообщений об ошибках и при этом сделать код более наглядным и лаконичным, то я этим способом воспользуюсь.
Цитата:
Сообщение от mazzy Посмотреть сообщение
мне кажется, что вместо такой хотелки стоит рассмотреть следующий стандартный подход...
Возможно, я не вполне четко выразился на счет "описателя табличных записей": я лично использую его для тех случаев, когда нужно вывести сообщение о чем-то из ряда вон выходящем, когда нужно реализовать по-быстрому трассировку работы того или иного функционала и т.п. В этом случае мне
  1. нужна необходимая и достаточная информация для однозначной идентификации записи
  2. даром не сдались SysInfoAction'ы просто потому, что и нужной формы может не быть (ох, как классно, когда с надеждой щелкаешь по ссылке в сообщении, а ничего не открывается!), и информация может писаться куда-нить в текстовый лог
Т.е. это чисто программистский инструмент, подобно вспомогательному классу отображения свойств объектов в отладчике.
Цитата:
Сообщение от mazzy Посмотреть сообщение
если юзать SysInfoAction то пользователю не нужно будет перевбивать в поиск recId - достаточно просто нажать на кнопку в инфологе.
Я бы ни за что не стал грузить пользователя такими подробностями, как RecId записи, более того, за считанным исключением те пользователи, с которыми я работаю, никогда не станут куда-то там перевбивать RecId - они инфологи-то зачастую не читают, какой уж там искать по RecId записи...
На счет setprefix - целиком и полностью поддерживаю.
Старый 02.09.2011, 11:45   #6  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Возможно, я не вполне четко выразился на счет "описателя табличных записей": я лично использую его для тех случаев, когда нужно вывести сообщение о чем-то из ряда вон выходящем, когда нужно реализовать по-быстрому трассировку работы того или иного функционала и т.п.
Попробуйте setpprefix - уверен, понравится.
__________________
полезное на axForum, github, vk, coub.
Старый 02.09.2011, 11:55   #7  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
Да не надо меня за setprefix() агитировать я им уже давно и успешно пользуюсь, однако, работать приходится не только со своим собственным кодом, но и с чужим, а там setprefix() может и не использоваться вовсе.
Старый 02.09.2011, 12:13   #8  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Да не надо меня за setprefix() агитировать я им уже давно и успешно пользуюсь, однако, работать приходится не только со своим собственным кодом, но и с чужим, а там setprefix() может и не использоваться вовсе.
значит - добавлять и в чужой код.
__________________
полезное на axForum, github, vk, coub.
Старый 02.09.2011, 12:53   #9  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,427 / 1771 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от gl00mie Посмотреть сообщение
Идея в том, чтобы сделать "говорящими" по возможности все логические выражения, не перегружая при этом код, что в сочетании с грамотным использованием setprefix() подчас просто творит чудеса Без подобных средств модификации зачастую пишутся так, что проверяется с десяток самых разных условий, делается пяток ветвлений в коде, и на выходе пользователь получает лишь скупое "обновление было отменено".
На самом деле хорошая идея. Главное правильно её применить. Ещё раз спасибо

P.S.: Вы не любите кошек? Да вы просто не умеете их готовить!
Старый 08.09.2011, 11:35   #10  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Вопрос в том, понравится ли такая модификация кода тому, кто будет поддерживать этот код после вас...
Хотя я, конечно, согласен, что в АХ не хватает методов такого плана. (Имхо, вы немного перебрали у себя с некоторыми из них, типа isMethodCalledCorrectly)
Для меня лично приемлимо только если assert - команда первого уровня
Старый 08.09.2011, 12:59   #11  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,867 / 3123 (112) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от kashperuk Посмотреть сообщение
Вопрос в том, понравится ли такая модификация кода тому, кто будет поддерживать этот код после вас...
Конечно понравится.
Ведь код будет написан единообразно и легче будет читаться. Сначала немного непривычно, а потом очень удобно.
Старый 08.09.2011, 14:59   #12  
plumbum is offline
plumbum
Участник
Соотечественники
 
182 / 86 (3) ++++
Регистрация: 07.12.2007
Адрес: Vienna, AT
было бы здорово, если бы в AX 2012 создали, например, атрибуты проверки вызова метода, или тех же args(), или в методе validateField на таблице с помощью атрибутов можно было указать простейшие проверки (типа "поле должно быть заполнено", или "значение должно быть больше/меньше").
Еще до выхода 2012 такие подходы использовались в C#...

@Kasperuk: Ваня, а что слышно по этому поводу "из кулуаров"?
__________________
http://www.axdevposts.blogspot.com
Пришел, уведел.... отойди, дай другому увидеть!
Старый 08.09.2011, 15:09   #13  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
О таких планах не слышал.
Аттрибуты в АХ появились только недавно, поэтому способы их применения пока не были детально рассмотреты, как я понимаю.
Старый 08.09.2011, 16:03   #14  
S.Kuskov is offline
S.Kuskov
Участник
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
 
3,427 / 1771 (66) ++++++++
Регистрация: 28.04.2007
Адрес: Калуга
Цитата:
Сообщение от plumbum Посмотреть сообщение
"поле должно быть заполнено", или "значение должно быть больше/меньше").
У полей таблицы есть свойство Mandatory, а у вещественных расширенных типов свойство AllowNegative

Понятно что хотелось бы большего. Например классно было бы помимо полей таблиц реализовать метод validate на самих расширенных типах. Тогда в него можно было бы запихнуть любые ограничения не говоря уже об ограничениях типа больше/меньше
Старый 08.09.2011, 20:22   #15  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
-Ваше политическое кредо?
-Всегда!
(с) "12 стульев"
Свойство Mandatory у полей таблиц работает безусловно; если логика заполнения поля настолько проста, разумеется, лучше использовать метаданные и положиться на ядро, но если логика предусматривает необходимость заполнения полей по определенным условиям, тут уже Mandatory не поможет. Аналогично с AllowNegative на расширенном типе: никакие расширенные типы не позволят автоматом проверять условия вида "при одном значении поля A сумма в поле B должна быть неотрицательной, а при другом - неположительной" или "значение в поле А должно быть меньше или равно значению в поле B". Кроме того, скажем, для полей-enum'ов нельзя задать на уровне метаданных какие-либо проверки, кроме обязательности заполнения (т.е. что нельзя выбрать значение enum'а, равное нулю), в то же время в коде порой приходится проверять условия вида "статус документа должен быть меньше, чем «Накладная»" - такие проверки в любом случае надо программировать.
Вообще же, дабы не раздувать дальнейшие дискуссии, открою небольшой секрет: изначально моей целью была публикация переписанного семейства классов SysExcel (см. Взаимодействие с Excel через .NET (семейство классов SysExcel), однако, эта модификация отчасти базировалась на двух других - представленных здесь классах проверки условий и утверждений и классе для преобразования значений между различными значимыми типами. Поскольку мне показалось, что эти две модификации представляют некоторую самостоятельную ценность, я опубликовал их отдельно. Это, в частности, позволит мне в дальнейшем не включать их во все публикуемые модификации, которые их используют, а просто делать ссылки в сообщении. Пользоваться ими в своей работе или же просто импортировать как довесок к семейству SysExcel (если для кого-то актуальна стабилизация работы с Excel), каждый может решить самостоятельно.
Я лично пришел к выводу, что от императивных проверок лучше переходить к проверкам декларативным, в идеале - описывать нужные граничные условия с помощью атрибутов, хотя в 2009-й они еще не поддерживаются. Часть декларативных проверок реализуется свойствами расширенных типов и полей, и этим безусловно надо пользоваться, но возможности эти покрывают лишь наиболее примитивные сценарии. Инструмент для более затейливых сценариев у меня получился вот таким, возможно, для кого-то он покажется чем-то чужеродным на фоне стандартного приложения, но мне он позволяет 1) писать больше проверок в коде, 2) писать их более лаконично и наглядно, 3) получать достаточно детализированные сообщения в случае нарушения проверяемых условий без необходимости писать логику вывода сообщений во всех тех местах кода, где выполняются проверки. Да, где-то нужно "подготовить контекст" для выводимых сообщений, тот же setprefix() вызвать, но в целом текстовых строк в коде получается меньше, чем при "традиционном" подходе с кучей if и checkFailed().

Последний раз редактировалось gl00mie; 08.09.2011 в 20:27. Причина: typo
Старый 08.09.2011, 22:03   #16  
Pustik is offline
Pustik
Участник
 
807 / 372 (14) ++++++
Регистрация: 04.06.2004
Цитата:
Сообщение от gl00mie Посмотреть сообщение
-Ваше политическое кредо?
-Всегда!
(с) "12 стульев"
За знание классики отдельное спасибо
__________________
-Ты в гномиков веришь?
-Нет.
-А они в тебя верят, смотри, не подведи их.
Старый 09.09.2011, 14:48   #17  
kia is offline
kia
Участник
 
96 / 19 (1) ++
Регистрация: 07.10.2008
Адрес: Харьков
Цитата:
Сообщение от mazzy Посмотреть сообщение
[*]ВСЕГДА надо писать setprefix("информация о том, что обрабатываем в данный момент - информация об источнике")
краткий совет - если не хватает информации об источнике ошибки, то всего-лишь добавьте setprefix в циклы верхнего уровня.
Иногда при больших циклах с воводом сообщений префикс перегружает лог.
И есть вопрос, может не совсем по SUBJ.
А как самому понизить уровень лога?
Например, в цикле выводятся сообщения не требующие уточнения, но если будет ошибка в table.validateWrite(), то хорошо бы вывести префикс с уточнением.
И, как выяснилось, оператор continue почему-то не сбрасывает уровень лога.
В результате может фигня получиться.
Старый 10.09.2011, 12:51   #18  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,684 / 5788 (200) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 3
В циклах setprefix отрабатывает только при первом вызове, поэтому чтобы менять префикс сообщений для каждой итерации, надо тело цикла выносить в отдельный метод и вызывать setprefix уже в нем, например, не в самом начале, а непосредственно перед table.validateWrite(), если уж так стоит задача.
За это сообщение автора поблагодарили: Logger (3).
Старый 06.07.2017, 23:27   #19  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,867 / 3123 (112) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от S.Kuskov Посмотреть сообщение
У полей таблицы есть свойство Mandatory, а у вещественных расширенных типов свойство AllowNegative

Понятно что хотелось бы большего. Например классно было бы помимо полей таблиц реализовать метод validate на самих расширенных типах. Тогда в него можно было бы запихнуть любые ограничения не говоря уже об ограничениях типа больше/меньше
Неплохо было бы иметь возможность для edt задавать значение по умолчанию.
Тогда если для ToDate задать значением по умолчанию maxDate() то намного удобнее было бы работать с прайсами и любыми табличками где есть FromDate и ToDate

Тогда ушли бы кривые условия вида
X++:
PriceDiscTable.fromDate <= transDate &&
(PriceDiscTable.ToDate    >= transDate || !PriceDiscTable.ToDate)
Можно было бы просто написать
X++:
PriceDiscTable.fromDate <= transDate &&
(PriceDiscTable.ToDate    >= transDate
Удобно и планы запросов прямее.

Хотя конечно этой цели можно и сейчас достичь, просто не так удобно.
Старый 07.07.2017, 07:18   #20  
dech is offline
dech
Участник
Аватар для dech
Самостоятельные клиенты AX
 
642 / 347 (13) ++++++
Регистрация: 25.06.2009
Адрес: Омск
Записей в блоге: 3
Когда мне надоело каждый раз писать обёртку try/catch со всякими assert permissions, я написал вот такой класс. В принципе - этого более чем достаточно для работы на чистом SQL. Думаю, код написан достаточно прозрачно, поясню только, что статические методы нужно использовать, если Connection и Statement не понадобятся при дальнейшем использовании. В противном случае, лучше создать экземпляр класса и использовать одно подключение, пока не закончим работать на SQL.
X++:
final class PPO_SafeSQL
{
    #define.InsufficientRights("Недостаточно прав для выполнения операции")

    Connection  connection;
    Statement   statement;
}

public void new()
{
;
    connection  = new Connection();
    statement   = connection.createStatement();
}

public server static ResultSet runQuery(str _sql)
{
    return new PPO_SafeSQL().executeQuery(_sql);
}

public server static void runUpdate(str _sql)
{
    new PPO_SafeSQL().executeUpdate(_sql);
}

public ResultSet executeQuery(str _sql)
{
    ResultSet ret;

    try
    {
        new SqlStatementExecutePermission(_sql).assert();

        // BP deviation documented
        ret = statement.executeQuery(_sql);

        CodeAccessPermission::revertAssert();
    }
    catch (Exception::CodeAccessSecurity)
    {
        throw error(#InsufficientRights);
    }

    return ret;
}

public void executeUpdate(str _sql)
{
    try
    {
        new SqlStatementExecutePermission(_sql).assert();

        // BP deviation documented
        statement.executeUpdate(_sql);

        CodeAccessPermission::revertAssert();
    }
    catch (Exception::CodeAccessSecurity)
    {
        throw error(#InsufficientRights);
    }
}
__________________
// no comments

Последний раз редактировалось dech; 07.07.2017 в 07:27.
Теги
download, законченный пример, полезное

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Как лучше оформлять несколько условий в select where? Повторная попытка mazzy DAX: Программирование 10 27.06.2011 13:54
Как лучше оформлять несколько условий в select where? mazzy DAX: Программирование 32 24.06.2011 20:40
Axapta 3.0 - можно ли править классы в USR слое AKIS DAX: Программирование 3 07.02.2004 01:19
Передача условий в отчет ArturK DAX: Программирование 4 18.08.2003 22:56
Системные классы Swetik DAX: Функционал 2 03.07.2003 12:11
Опции темы Поиск в этой теме
Поиск в этой теме:

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

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

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

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