Miklix

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

Опубликовано: 16 февраля 2025 г. в 00:25:57 UTC
Последнее обновление: 12 января 2026 г. в 08:43:14 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 часто приходится сталкиваться с созданием иерархии классов, в которой каждый подкласс соответствует значению перечисления или имеет другую связь с данными. Классический подход заключается в создании метода-конструктора в суперклассе, который содержит оператор switch, определяющий, какой класс следует создать в зависимости от входных данных.

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

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

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

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

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

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

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

  • MyProcessorMyProcessor_SalesMyProcessor_Sales_ReservOrderedMyProcessor_Sales_ReservPhysical

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

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

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

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

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-корпорации. Когда он не ведет блог, то тратит свое свободное время на огромное количество интересов, хобби и занятий, что в некоторой степени отражается в разнообразии тем, освещаемых на этом сайте.