Използване на рамката SysExtension, за да разберете кой подклас да създадем в Dynamics AX 2012
Публикувано: 16 февруари 2025 г. в 0:25:32 ч. 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 или има някакво друго свързване на данни. Класическият дизайн е след това да има метод за конструиране в супер класа, който има превключвател, който определя кой клас да се инстанцира въз основа на входа.
Това работи добре по принцип, но ако имате много различни възможни входни данни (много елементи в enum или може би входът е комбинация от няколко различни стойности), може да стане досаден и податлив на грешки за поддържане, а дизайнът винаги има недостатъка, че ще трябва да промените споменатия метод на конструиране, ако някога добавите нов подклас или направите промени в кой подклас трябва да се използва въз основа на кой вход.
За щастие има много по-елегантен, но за съжаление и много по-малко известен начин да се направи това, а именно чрез използването на рамката SysExtension.
Тази рамка се възползва от атрибутите, които можете да използвате, за да украсите вашите подкласове, за да накарате системата да може да разбере кой подклас трябва да се използва за обработка на какво. Все още ще ви е необходим метод за конструкция, но ако е направен правилно, никога няма да се налага да го променяте, когато добавяте нови подкласове.
Нека разгледаме един въображаем пример и да кажем, че ще имплементираме йерархия, която извършва някакъв вид обработка въз основа на таблицата InventTrans. Коя обработка да се извърши зависи от StatusReceipt и StatusIssue на записите, както и от това дали записите са свързани със SalesLine, PurchLine или нито едното, нито другото. Вече гледате много различни комбинации.
Нека тогава кажем, че знаете, че засега трябва да се справите само с няколко комбинации, но също така знаете, че ще бъдете помолени да можете да обработвате все повече и повече комбинации с течение на времето.
Нека го направим сравнително просто и да кажем, че засега трябва да обработвате само записи, свързани със SalesLine със StatusIssue на ReservPhysical или ReservOrdered, всички други комбинации засега могат да бъдат игнорирани, но тъй като знаете, че ще трябва да се справите с тях по-късно, ще искате да проектирате кода си по начин, който го прави лесно разширяем.
Засега йерархията ви може да изглежда така:
- Моят процесор
- MyProcessor_Sales
- MyProcessor_Sales_ReservOrdered
- MyProcessor_Sales_ReservPhysical
- MyProcessor_Sales
Сега можете лесно да имплементирате метод в супер класа, който създава екземпляр на подклас, базиран на ModuleInventPurchSales и enum 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 метод за всеки член на данни, но аз ги пропуснах тук, тъй като съм сигурен, че знаете как да го направите - в противен случай нека го считаме за упражнение ;-)
Сега можете да използвате вашия клас атрибути, за да украсите всеки от вашите класове за обработка. Например декларациите на класовете могат да изглеждат така:
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) можете да добавите метод за конструкция като този:
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 разчита на кеширане, така че в работеща система се съмнявам, че има значителен удар върху производителността. Отстраняване на неизправности: Ако методът на конструкцията не намери вашите подкласове, въпреки че сте сигурни, че са декорирани правилно, това може да е проблем с кеширането. Опитайте да изчистите кешовете както на клиента, така и на сървъра. Не би трябвало да е необходимо действително да рестартирате AOS, но може да е крайна мярка.