Miklix

Использование SysExtension Framework для определения подкласса, экземпляр которого необходимо создать в Dynamics AX 2012

Опубликовано: 16 февраля 2025 г. в 00:25:57 UTC

В этой статье описывается, как использовать малоизвестную платформу SysExtension в Dynamics AX 2012 и Dynamics 365 for Operations для создания экземпляров подклассов на основе декораций атрибутов, что позволяет легко расширять дизайн иерархии классов обработки.


Эта страница была переведена с английского языка для того, чтобы сделать ее доступной как можно большему числу людей. К сожалению, машинный перевод еще не является совершенной технологией, поэтому возможны ошибки. Если вы хотите, вы можете просмотреть оригинальную английскую версию здесь:

Using the SysExtension Framework to Find Out Which Subclass to Instantiate in Dynamics AX 2012

Информация в этом посте основана на Dynamics AX 2012 R3. Она может быть верна или нет для других версий. (Обновление: я могу подтвердить, что информация в этой статье также верна для Dynamics 365 for Operations)

При реализации классов обработки в Dynamics AX вы часто сталкиваетесь с созданием иерархии классов, в которой каждый подкласс соответствует значению enum или имеет некоторую другую связь данных. Классический дизайн заключается в том, чтобы затем иметь метод построения в суперклассе, который имеет переключатель, определяющий, какой класс создавать на основе входных данных.

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

К счастью, есть гораздо более элегантный, но, к сожалению, и гораздо менее известный способ сделать это, а именно, с помощью фреймворка SysExtension.

Этот фреймворк использует атрибуты, которые вы можете использовать для декорирования ваших подклассов, чтобы система могла определить, какой подкласс должен использоваться для обработки чего. Вам все равно понадобится метод конструкта, но если все сделано правильно, вам никогда не придется изменять его при добавлении новых подклассов.

Давайте рассмотрим воображаемый пример и предположим, что вы собираетесь реализовать иерархию, которая выполняет некоторую обработку на основе таблицы InventTrans. Какую обработку выполнять, зависит от StatusReceipt и StatusIssue записей, а также от того, связаны ли записи с SalesLine, PurchLine или ни с тем, ни с другим. Уже сейчас вы смотрите на множество различных комбинаций.

Предположим, вы знаете, что на данный момент вам нужно справиться только с несколькими комбинациями, но вы также знаете, что со временем вам придется справляться со все большим количеством комбинаций.

Давайте сохраним простоту и предположим, что на данный момент вам нужно обрабатывать только записи, связанные с SalesLine со значением StatusIssue ReservPhysical или ReservOrdered, все остальные комбинации пока можно игнорировать, но поскольку вы знаете, что вам придется обрабатывать их позже, вам следует разработать свой код таким образом, чтобы его можно было легко расширять.

На данный момент ваша иерархия может выглядеть примерно так:

  • МойПроцессор
    • MyProcessor_Sales
      • MyProcessor_Sales_ReservedЗаказано
      • MyProcessor_Sales_ReservPhysical

Теперь вы можете легко реализовать метод в суперклассе, который создает экземпляр подкласса на основе ModuleInventPurchSales и перечисления StatusIssue. Но тогда вам придется изменять суперкласс каждый раз, когда вы добавляете подкласс, и это не совсем идея наследования в объектно-ориентированном программировании. В конце концов, вам не нужно изменять RunBaseBatch или SysOperationServiceBase каждый раз, когда вы добавляете новое пакетное задание.

Вместо этого вы можете использовать фреймворк SysExtension. Это потребует от вас добавления еще одного класса, который должен расширять SysAttribute. Этот класс будет использоваться как атрибут, которым вы можете декорировать свои классы обработки.

Этот класс очень похож на класс контракта данных для реализации SysOperation, в том смысле, что он будет иметь некоторые члены данных и методы parm для получения и установки этих значений.

В нашем случае ClassDeclaration может выглядеть примерно так:

class MyProcessorSystemAttribute extends SysAttribute
{
    ModuleInventPurchSales  module;
    StatusIssue             statusIssue;
    StatusReceipt           statusReceipt
}

Вам нужно создать метод new() для создания экземпляров всех членов данных. Если хотите, можете дать некоторым или всем из них значения по умолчанию, но я этого не делал.

public void new(ModuleInventPurchSales  _module,
                StatusIssue             _statusIssue,
                StatusReceipt           _statusReceipt)
{
    ;

    super();

    module          = _module;
    statusIssue     = _statusIssue;
    statusReceipt   = _statusReceipt;
}

И вам также следует реализовать метод parm для каждого члена данных, но я опустил его здесь, поскольку уверен, что вы знаете, как это сделать. В противном случае давайте будем считать это упражнением ;-)

Теперь вы можете использовать свой класс атрибутов для декорирования каждого из ваших классов обработки. Например, объявления классов могут выглядеть так:

[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
                            StatusIssue::None,
                            StatusReceipt::None)]
class MyProcessor_Sales extends MyProcessor
{
}

[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
                            StatusIssue::ReservOrdered,
                            StatusReceipt::None)]
class MyProcessor_Sales_ReservOrdered extends MyProcessor_Sales
{
}

[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
                            StatusIssue::ReservPhysical,
                            StatusReceipt::None)]
class MyProcessor_Sales_ReservPhysical extends MyProcessor_Sales
{
}

Конечно, вы можете называть свои классы как угодно, главное, чтобы вы снабжали свои классы атрибутами, соответствующими типу обработки, которую они выполняют. (Но имейте в виду, что в Dynamics AX существуют соглашения об именовании иерархий классов, и всегда полезно следовать им, если это возможно).

Теперь, когда вы декорировали свои классы, чтобы определить, какой тип обработки выполняет каждый из них, вы можете воспользоваться фреймворком SysExtension для создания экземпляров объектов подклассов по мере необходимости.

В вашем суперклассе (MyProcessor) вы можете добавить метод конструкции, подобный этому:

public static MyProcessor construct(ModuleInventPurchSales _module,
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
    MyProcessor                 ret;
    MyProcessorSystemAttribute  attribute;
    ;

    attribute = new MyProcessorSystemAttribute( _module,
                                                _statusIssue,
                                                _statusReceipt);

    ret = SysExtensionAppClassFactory::getClassFromSysAttribute(classStr(MyProcessor), attribute);

    if (!ret)
    {
        //  no class found
        //  here you could throw an error, instantiate a default
        //  processor instead, or just do nothing, up to you
    }

    return ret;
}

Действительно интересная часть — и на самом деле объект (простите за каламбур) всего этого поста — это метод getClassFromSysAttribute() в классе SysExtensionAppClassFactory. Этот метод принимает имя суперкласса иерархии (и этот суперкласс не обязательно должен быть наверху иерархии; это просто означает, что только классы, расширяющие этот класс, будут иметь право) и объект атрибута.

Затем он возвращает объект класса, который расширяет указанный суперкласс и снабжен соответствующим атрибутом.

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

Что насчет производительности? Честно говоря, я не пытался проводить бенчмаркинг, но мое внутреннее чувство подсказывает, что это, вероятно, работает хуже, чем классический дизайн оператора switch. Однако, учитывая, что большинство проблем с производительностью в Dynamics AX вызвано доступом к базе данных, я бы не стал слишком беспокоиться об этом.

Конечно, если вы реализуете что-то, что потребует быстрого создания тысяч объектов, вы можете захотеть исследовать дальше, но в классических случаях, когда вы просто создаете экземпляр одного объекта для выполнения длительной обработки, я сомневаюсь, что это будет иметь значение. Кроме того, учитывая мой совет по устранению неполадок (следующий абзац), похоже, что фреймворк SysExtension полагается на кэширование, поэтому я сомневаюсь, что в работающей системе он значительно повлияет на производительность. Устранение неполадок: если метод конструкта не находит ваши подклассы, даже если вы уверены, что они правильно оформлены, это может быть проблема кэширования. Попробуйте очистить кэши как на клиенте, так и на сервере. Фактически перезапускать AOS не обязательно, но это может быть последним средством.

Поделиться на BlueskyПоделиться на FacebookПоделиться на LinkedInПоделиться на TumblrПоделиться на XПоделиться на LinkedInЗакрепить на Pinterest

Миккель Банг Кристенсен

Об авторе

Миккель Банг Кристенсен
Миккель - создатель и владелец сайта miklix.com. Он имеет более чем 20-летний опыт работы в качестве профессионального программиста/разработчика программного обеспечения и в настоящее время работает на полную ставку в крупной европейской IT-корпорации. Когда он не ведет блог, то тратит свое свободное время на огромное количество интересов, хобби и занятий, что в некоторой степени отражается в разнообразии тем, освещаемых на этом сайте.