Miklix

Використання фреймворку SysExtension для визначення екземпляра якого підкласу в Dynamics AX 2012

Опубліковано: 16 лютого 2025 р. о 00:26:03 UTC
Останнє оновлення: 12 січня 2026 р. о 08:43:22 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 ви часто стикаєтеся зі створенням ієрархії класів, у якій кожен підклас відповідає значенню перерахування або має якийсь інший зв'язок даних. Класичний варіант полягає в тому, щоб у надкласі був метод конструкції, який має перемикач, що визначає, який клас створювати на основі вхідних даних.

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

На щастя, існує набагато елегантніший, але, на жаль, також набагато менш відомий спосіб зробити це, а саме використання фреймворку SysExtension.

Цей фреймворк використовує атрибути, які можна використовувати для декорування підкласів, щоб система могла визначити, який підклас слід використовувати для обробки чого. Вам все одно знадобиться метод конструкції, але якщо все зроблено правильно, вам ніколи не доведеться його змінювати під час додавання нових підкласів.

Розглянемо уявний приклад. Припустимо, що ви збираєтеся реалізувати ієрархію, яка виконує певний тип обробки на основі таблиці InventTrans. Вибір обробки залежить від записів StatusReceipt та StatusIssue, а також від того, чи пов'язані записи з SalesLine, PurchLine чи ні. Вже зараз ви розглядаєте багато різних комбінацій.

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

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

Ваша ієрархія може виглядати приблизно так на даний момент:

  • МійПроцесорМійПроцесор_ПродажіМійПроцесор_Резерв_ПродажівЗамовленоМійПроцесор_Резерв_ПродажівФізичний

Тепер ви можете легко реалізувати метод у суперкласі, який створює екземпляр підкласу на основі ModuleInventPurchSales та перерахування StatusIssue. Але тоді вам потрібно буде змінювати суперклас щоразу, коли ви додаєте підклас, і це насправді не є ідеєю успадкування в об'єктно-орієнтованому програмуванні. Зрештою, вам не потрібно змінювати RunBaseBatch або SysOperationServiceBase щоразу, коли ви додаєте нове пакетне завдання.

Замість цього ви можете скористатися фреймворком SysExtension. Це вимагатиме від вас додавання ще одного класу, який має розширювати SysAttribute. Цей клас буде використовуватися як атрибут, яким ви можете декорувати свої класи обробки.

Цей клас дуже схожий на те, як ви б створили клас контракту даних для реалізації SysOperation, оскільки він матиме деякі члени даних та методи parm для отримання та встановлення цих значень.

У нашому випадку оголошення класу може виглядати приблизно так:

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. Цей метод приймає назву суперкласу ієрархії (і цей суперклас не обов'язково має бути на вершині ієрархії; це просто означає, що будуть придатні лише класи, що розширюють цей клас) та об'єкт атрибута.

Потім він повертає об'єкт класу, який розширює зазначений суперклас і декорований відповідним атрибутом.

Звичайно, ви можете додати стільки додаткової валідації чи логіки до методу construct, скільки забажаєте, але важливий висновок полягає в тому, що після реалізації вам більше ніколи не доведеться змінювати цей метод. Ви можете додавати підкласи до ієрархії, і якщо ви переконаєтеся, що декоруєте їх належним чином, метод construct знайде їх, навіть якщо їх не існувало на момент написання.

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

Звісно, якщо ви реалізуєте щось, що вимагатиме швидкого створення тисяч об'єктів, можливо, вам варто дослідити це детальніше, але в класичних випадках, коли ви просто створюєте екземпляр одного об'єкта для виконання тривалої обробки, я сумніваюся, що це матиме значення. Також, враховуючи мою пораду щодо усунення несправностей (наступний абзац), схоже, що фреймворк SysExtension покладається на кешування, тому в працюючій системі я сумніваюся, що це матиме значне падіння продуктивності.

Виправлення неполадок: Якщо метод construct не знаходить ваші підкласи, навіть якщо ви впевнені, що вони оформлені правильно, це може бути проблема з кешуванням. Спробуйте очистити кеш як на клієнті, так і на сервері. Перезапуск AOS не повинен бути обов'язковим, але це може бути крайнім заходом.

Додаткова література

Якщо вам сподобався цей пост, вам також можуть сподобатися ці пропозиції:


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

Міккель Крістенсен

Про автора

Міккель Крістенсен
Міккель - творець і власник сайту miklix.com. Він має понад 20 років досвіду роботи професійним програмістом/розробником програмного забезпечення і наразі працює на повну ставку у великій європейській ІТ-корпорації. У вільний від ведення блогу час він присвячує різноманітним інтересам, хобі та захопленням, що певною мірою відображається на різноманітності тем, які висвітлюються на цьому сайті.