استفاده از چارچوب SysExtension برای یافتن اینکه کدام زیر کلاس را در Dynamics AX 2012 نمونه سازی کنیم
منتشر شده: ۱۶ فوریهٔ ۲۰۲۵ ساعت ۰:۲۶:۱۱ (UTC)
این مقاله نحوه استفاده از چارچوب کمشناخته SysExtension را در Dynamics AX 2012 و Dynamics 365 برای عملیات برای نمونهسازی زیر کلاسها بر اساس تزئینات مشخصه توضیح میدهد که امکان طراحی آسان سلسله مراتب کلاس پردازش را فراهم میکند.
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 مطابقت دارد یا دارای برخی دادههای دیگر است. یک طراحی کلاسیک این است که یک متد ساخت در کلاس super داشته باشد، که دارای یک سوئیچ است که تعیین می کند کدام کلاس را بر اساس ورودی نمونه سازی کند.
این در اصل به خوبی کار میکند، اما اگر ورودیهای ممکن زیادی داشته باشید (بسیاری از عناصر در یک enum یا شاید ورودی ترکیبی از چندین مقدار مختلف باشد)، حفظ آن ممکن است خستهکننده و مستعد خطا باشد و طراحی همیشه این عیب را دارد که اگر یک زیر کلاس جدید اضافه کنید یا تغییراتی در کدام زیر کلاس باید ایجاد کنید، همیشه این اشکال را دارد که باید روش ساخت گفته شده را تغییر دهید.
خوشبختانه، روشی بسیار زیباتر، اما متاسفانه بسیار کمتر شناخته شده برای انجام این کار وجود دارد، یعنی استفاده از چارچوب SysExtension.
این فریم ورک از ویژگیهایی بهره میبرد که میتوانید از آنها برای تزئین کلاسهای فرعی خود استفاده کنید تا سیستم بتواند تشخیص دهد که کدام کلاس فرعی باید برای رسیدگی به چه چیزی استفاده شود. شما همچنان به یک متد ساختنی نیاز دارید، اما اگر به درستی انجام شود، هرگز مجبور نخواهید بود آن را هنگام اضافه کردن زیر کلاسهای جدید تغییر دهید.
بیایید به یک مثال خیالی نگاه کنیم و بگوییم که شما قصد دارید سلسله مراتبی را پیاده سازی کنید که نوعی پردازش را بر اساس جدول InventTrans انجام می دهد. اینکه کدام پردازش باید انجام شود بستگی به StatusReceipt و StatusIssue سوابق دارد، و همچنین به اینکه آیا رکوردها به SalesLine، PurchLine یا هیچکدام مربوط هستند. در حال حاضر، شما به تعداد زیادی ترکیب مختلف نگاه می کنید.
بگذارید بگوییم که میدانید در حال حاضر فقط باید تعداد انگشت شماری از ترکیبها را مدیریت کنید، اما همچنین میدانید که از شما خواسته میشود که بتوانید ترکیبهای بیشتری را در طول زمان مدیریت کنید.
بیایید آن را نسبتاً ساده نگه داریم و بگوییم که در حال حاضر فقط باید رکوردهای مربوط به SalesLine را با یک StatusIssue ReservPhysical یا ReservOrdered مدیریت کنید، تمام ترکیبات دیگر را می توان فعلا نادیده گرفت، اما از آنجایی که می دانید بعداً مجبور خواهید بود آنها را مدیریت کنید، باید کد خود را به گونه ای طراحی کنید که به راحتی قابل توسعه باشد.
سلسله مراتب شما ممکن است در حال حاضر چیزی شبیه به این باشد:
- MyProcessor
- MyProcessor_Sales
- MyProcessor_Sales_Reservordered
- MyProcessor_Sales_ReservPhysical
- MyProcessor_Sales
اکنون، میتوانید به راحتی متدی را در کلاس super پیادهسازی کنید که یک زیر کلاس را بر اساس یک 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 را برای هر عضو داده پیادهسازی کنید، اما من آنها را در اینجا حذف کردهام زیرا مطمئن هستم که شما میدانید چگونه این کار را انجام دهید - در غیر این صورت، بیایید آن را یک تمرین در نظر بگیریم ;-)
اکنون می توانید از کلاس ویژگی خود برای تزئین هر یک از کلاس های پردازشی خود استفاده کنید. به عنوان مثال، اعلان های کلاس می تواند به شکل زیر باشد:
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 است. کاری که این روش انجام می دهد این است که نام کلاس فوق العاده یک سلسله مراتب را می پذیرد (و نیازی نیست که این کلاس فوق العاده در بالای سلسله مراتب باشد؛ به سادگی به این معنی است که فقط کلاس هایی که این کلاس را گسترش می دهند واجد شرایط هستند) و یک شی ویژگی.
سپس یک شی از یک کلاس را برمی گرداند که کلاس فوق العاده مشخص شده را گسترش می دهد و با یک ویژگی مربوطه تزئین می شود.
بدیهی است که میتوانید اعتبار یا منطق بیشتری را به روش ساخت که میخواهید اضافه کنید، اما نکته مهم در اینجا این است که پس از پیادهسازی، دیگر نباید این روش را تغییر دهید. شما می توانید زیر کلاس ها را به سلسله مراتب اضافه کنید و تا زمانی که مطمئن شوید که آنها را به درستی تزئین کرده اید، متد ساخت آنها را پیدا می کند حتی اگر در زمان نوشته شدن آن وجود نداشتند.
در مورد عملکرد چطور؟ من صادقانه سعی نکردهام آن را محک بزنم، اما احساس اصلی من این است که احتمالاً این طراحی بدتر از طراحی بیانیه سوئیچ کلاسیک است. با این حال، با توجه به اینکه بیشترین مشکلات عملکرد در Dynamics AX به دلیل دسترسی به پایگاه داده ایجاد می شود، من زیاد نگران آن نیستم.
البته، اگر چیزی را پیادهسازی میکنید که نیاز به هزاران شی برای ایجاد سریع دارد، ممکن است بخواهید بیشتر بررسی کنید، اما در موارد کلاسیک که فقط یک شی را برای انجام پردازش طولانی نمونهسازی میکنید، شک دارم که مهم باشد. همچنین، با توجه به نکته عیبیابی من (پارگراف بعدی)، به نظر میرسد که چارچوب SysExtension به ذخیرهسازی متکی است، بنابراین در یک سیستم در حال اجرا شک دارم که عملکرد قابل توجهی داشته باشد. عیبیابی: اگر روش ساخت کلاسهای فرعی شما را پیدا نمیکند، حتی اگر مطمئن هستید که آنها به درستی تزئین شدهاند، ممکن است مشکل در حافظه پنهان باشد. سعی کنید کش ها را هم روی کلاینت و هم روی سرور پاک کنید. در واقع نباید نیازی به راه اندازی مجدد AOS باشد، اما ممکن است آخرین راه حل باشد.