Використання фреймворку SysExtension для визначення екземпляра якого підкласу в Dynamics AX 2012
Опубліковано: 16 лютого 2025 р. о 00:26:03 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 of ReservPhysical або ReservOrdered, всі інші комбінації поки що можна ігнорувати, але оскільки ви знаєте, що вам доведеться обробляти їх пізніше, ви захочете розробити свій код таким чином, щоб зробити його легко розширюваним.
Наразі ваша ієрархія може виглядати приблизно так:
- MyProcessor
- MyProcessor_Sales
- MyProcessor_Sales_ReservOrdered
- MyProcessor_Sales_ReservPhysical
- MyProcessor_Sales
Тепер ви можете легко реалізувати метод у суперкласі, який створює екземпляр підкласу на основі ModuleInventPurchSales та переліку StatusIssue. Але тоді вам потрібно буде змінювати суперклас кожного разу, коли ви додаєте підклас, і це насправді не є ідеєю успадкування в об'єктно-орієнтованому програмуванні. Зрештою, вам не потрібно змінювати RunBaseBatch або SysOperationServiceBase щоразу, коли ви додаєте нове пакетне завдання.
Замість цього ви можете використовувати фреймворк SysExtension. Це вимагатиме від вас додати ще один клас, який має розширити SysAttribute. Цей клас буде використовуватися як атрибут, яким ви можете прикрасити свої заняття обробки.
Цей клас дуже схожий на те, як би ви створили клас контракту даних для реалізації SysOperation, в тому сенсі, що він буде мати деякі елементи даних і методи parm для отримання і встановлення цих значень.
У нашому випадку ClassDeclaration може виглядати приблизно так:
{
ModuleInventPurchSales module;
StatusIssue statusIssue;
StatusReceipt statusReceipt
}
Вам потрібно створити метод new() для створення екземплярів усіх елементів даних. Якщо ви бажаєте, ви можете дати деяким або всім з них значення за замовчуванням, але я цього не зробив.
StatusIssue _statusIssue,
StatusReceipt _statusReceipt)
{
;
super();
module = _module;
statusIssue = _statusIssue;
statusReceipt = _statusReceipt;
}
І ви також повинні впровадити метод parm для кожного члена даних, але я опустив їх тут, оскільки я впевнений, що ви знаєте, як це робити - в іншому випадку, давайте вважатимемо це вправою ;-)
Тепер ви можете використовувати свій атрибут class для прикраси кожного з ваших класів обробки. Наприклад, оголошення класу можуть виглядати так:
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) ви можете додати метод construct таким чином:
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 знайде їх, навіть якщо вони не існували на момент його написання.
А як щодо продуктивності? Чесно кажучи, я не намагався провести порівняльний аналіз, але моє внутрішнє відчуття полягає в тому, що це, ймовірно, працює гірше, ніж класичний дизайн перемикача. Однак, враховуючи, що на сьогоднішній день більшість проблем з продуктивністю в Dynamics AX викликані доступом до бази даних, я б не надто хвилювався з цього приводу.
Звичайно, якщо ви реалізуєте щось, що вимагатиме швидкого створення тисяч об'єктів, ви можете захотіти дослідити далі, але в класичних випадках, коли ви просто створюєте екземпляр одного об'єкта для тривалої обробки, я сумніваюся, що це матиме значення. Крім того, враховуючи мою пораду щодо усунення несправностей (наступний абзац), виявляється, що фреймворк SysExtension покладається на кешування, тому в працюючій системі я сумніваюся, що він має значний вплив на продуктивність. Усунення несправностей: Якщо метод construct не знаходить ваші підкласи, хоча ви впевнені, що вони оформлені правильно, це може бути проблемою кешування. Спробуйте очистити кеш як на клієнті, так і на сервері. Насправді перезапускати AOS не потрібно, але це може бути крайнім заходом.