استخدام إطار عمل SysExtension لمعرفة الفئة الفرعية التي يجب إنشاؤها في Dynamics AX 2012
نُشرت: ١٦ فبراير ٢٠٢٥ م في ١٢:٢٥:٢٢ ص 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 مع StatusIssue من ReservPhysical أو ReservOrdered، ويمكن تجاهل جميع التركيبات الأخرى في الوقت الحالي، ولكن نظرًا لأنك تعلم أنه يتعين عليك التعامل معها لاحقًا، فستحتاج إلى تصميم الكود الخاص بك بطريقة تجعله قابلاً للتوسيع بسهولة.
قد يبدو التسلسل الهرمي الخاص بك مثل هذا في الوقت الحالي:
- معالجي
- مبيعات المعالج الخاص بي
- تم حجز مبيعات المعالج الخاص بي
- معالجي_احتياطي_المبيعات_الفيزيائي
- مبيعات المعالج الخاص بي
الآن، يمكنك بسهولة تنفيذ طريقة في الفئة العليا التي تنشئ فئة فرعية استنادًا إلى ModuleInventPurchSales وStatusIssue enum. ولكنك ستحتاج بعد ذلك إلى تعديل الفئة العليا في كل مرة تضيف فيها فئة فرعية، وهذه ليست فكرة الميراث في البرمجة الموجهة للكائنات. بعد كل شيء، لست بحاجة إلى تعديل 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 بالفعل، ولكن قد يكون الملاذ الأخير.