Използване на рамката SysExtension, за да разберете кой подклас да създадем в Dynamics AX 2012
Публикувано: 16 февруари 2025 г. в 0:25:32 ч. UTC
Последна актуализация: 12 януари 2026 г. в 8:42:50 ч. 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, всички други комбинации могат да бъдат игнорирани засега, но тъй като знаете, че ще трябва да ги обработвате по-късно, ще искате да проектирате кода си по начин, който го прави лесно разширяем.
Вашата йерархия може да изглежда така засега:
- МоятПроцесорМоятПроцесорПродажбиМоятПроцесорРезерв_ПродажбиПоръчанМоятПроцесорРезерв_ПродажбиФизически
Сега лесно можете да имплементирате метод в суперкласа, който създава подклас, базиран на ModuleInventPurchSales и изброяване StatusIssue. Но след това ще трябва да променяте суперкласа всеки път, когато добавяте подклас, а това не е истинската идея за наследяване в обектно-ориентираното програмиране. В крайна сметка, не е нужно да променяте RunBaseBatch или SysOperationServiceBase всеки път, когато добавяте нова пакетна задача.
Вместо това можете да използвате рамката SysExtension. Това ще изисква да добавите друг клас, който трябва да разшири SysAttribute. Този клас ще се използва като атрибут, с който можете да декорирате вашите класове за обработка.
Този клас е много подобен на начина, по който бихте създали клас за договор за данни за имплементация на SysOperation, тъй като ще има някои членове на данни и parm методи за получаване и задаване на тези стойности.
В нашия случай, декларацията на класа може да изглежда по следния начин:
{
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, но важното е, че веднъж внедрен, никога повече не би трябвало да променяте този метод. Можете да добавяте подкласове към йерархията и стига да се уверите, че сте ги декорирали по подходящ начин, методът construct ще ги намери, дори и да не са съществували, когато е бил написан.
Ами производителността? Честно казано, не съм се опитвал да я сравнявам, но интуитивно усещам, че това вероятно се представя по-зле от класическия дизайн с оператори switch. Като се има предвид обаче, че повечето проблеми с производителността в Dynamics AX са причинени от достъпа до базата данни, не бих се притеснявал твърде много за това.
Разбира се, ако имплементирате нещо, което ще изисква бързо създаване на хиляди обекти, може би е добре да проучите по-подробно, но в класическите случаи, когато просто създавате един-единствен обект, за да извършите продължителна обработка, се съмнявам, че това ще има значение. Също така, като се има предвид моят съвет за отстраняване на неизправности (следващия параграф), изглежда, че рамката SysExtension разчита на кеширане, така че в работеща система се съмнявам, че има значително влияние върху производителността.
Отстраняване на неизправности: Ако методът construct не намери вашите подкласове, въпреки че сте сигурни, че са декорирани правилно, може да е проблем с кеширането. Опитайте да изчистите кеша както на клиента, така и на сървъра. Не би трябвало да е необходимо да рестартирате AOS, но може да е последна мярка.
Допълнително четене
Ако ви е харесала тази публикация, може да ви харесат и тези предложения:
- Грешка „Няма дефиниран клас метаданни за обект на договор за данни“ в Dynamics AX 2012
- Идентифициране на клас документ и заявка за AIF услуга в Dynamics AX 2012
- Бърз преглед на Dynamics AX 2012 SysOperation Framework
