การใช้เฟรมเวิร์ก SysExtension เพื่อค้นหาว่าคลาสย่อยที่จะสร้างอินสแตนซ์ใน Dynamics AX 2012
ที่ตีพิมพ์: 16 กุมภาพันธ์ 2025 เวลา 0 นาฬิกา 26 นาที 09 วินาที UTC
ปรับปรุงล่าสุด : 12 มกราคม 2026 เวลา 8 นาฬิกา 43 นาที 26 วินาที 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 หรือมีการเชื่อมโยงข้อมูลแบบอื่น การออกแบบแบบคลาสสิกคือการมีเมธอด construct ในคลาสแม่ ซึ่งมี switch ที่ใช้กำหนดว่าควรสร้างอินสแตนซ์ของคลาสใดโดยขึ้นอยู่กับอินพุต
โดยหลักการแล้ว วิธีนี้ใช้ได้ดี แต่ถ้าคุณมีอินพุตที่เป็นไปได้หลายแบบ (เช่น มีองค์ประกอบหลายอย่างใน enum หรืออินพุตอาจเป็นการผสมผสานของค่าต่างๆ หลายค่า) การบำรุงรักษาอาจยุ่งยากและมีโอกาสเกิดข้อผิดพลาดได้ง่าย และการออกแบบนี้มักมีข้อเสียตรงที่คุณจะต้องแก้ไขวิธีการสร้างดังกล่าวหากคุณเพิ่มคลาสย่อยใหม่หรือทำการเปลี่ยนแปลงว่าควรใช้คลาสย่อยใดโดยอิงจากอินพุตใด
โชคดีที่มีวิธีที่ดูดีกว่ามาก แต่โชคร้ายที่วิธีนี้ไม่ค่อยเป็นที่รู้จักนัก นั่นคือการใช้เฟรมเวิร์ก SysExtension
เฟรมเวิร์กนี้ใช้ประโยชน์จากแอตทริบิวต์ที่คุณสามารถใช้ตกแต่งคลาสย่อยของคุณ เพื่อให้ระบบสามารถระบุได้ว่าควรใช้คลาสย่อยใดในการจัดการอะไร คุณยังคงต้องมีเมธอดสร้าง แต่ถ้าทำอย่างถูกต้อง คุณจะไม่ต้องแก้ไขมันอีกเลยเมื่อเพิ่มคลาสย่อยใหม่
ลองพิจารณาตัวอย่างสมมุติ สมมติว่าคุณกำลังจะสร้างลำดับชั้นที่ประมวลผลข้อมูลในตาราง InventTrans การประมวลผลที่จะทำนั้นขึ้นอยู่กับสถานะการรับ (StatusReceipt) และสถานะการออก (StatusIssue) ของเรคอร์ด รวมถึงว่าเรคอร์ดเหล่านั้นเกี่ยวข้องกับรายการขาย (SalesLine) รายการซื้อ (PurchLine) หรือไม่เกี่ยวข้องกับทั้งสองอย่างเลย แค่นี้คุณก็เห็นความเป็นไปได้หลายแบบแล้ว
สมมติว่าตอนนี้คุณรู้ว่าคุณจำเป็นต้องจัดการแค่เพียงไม่กี่ชุดค่าผสม แต่คุณก็รู้ว่าในอนาคตคุณจะต้องสามารถจัดการชุดค่าผสมที่มากขึ้นเรื่อยๆ
เพื่อให้เข้าใจง่ายขึ้น ในตอนนี้เราจะจัดการเฉพาะข้อมูลที่เกี่ยวข้องกับ SalesLine ที่มี StatusIssue เป็น ReservPhysical หรือ ReservOrdered เท่านั้น ส่วนค่าผสมอื่นๆ สามารถละเว้นไปก่อนได้ แต่เนื่องจากคุณรู้ว่าคุณจะต้องจัดการกับค่าผสมเหล่านั้นในภายหลัง คุณจึงควรออกแบบโค้ดของคุณให้สามารถขยายได้ง่ายในอนาคต
โครงสร้างลำดับชั้นของคุณในตอนนี้อาจมีลักษณะดังนี้:
- MyProcessorMyProcessor_SalesMyProcessor_Sales_ReservOrderedMyProcessor_Sales_ReservPhysical
ตอนนี้ คุณสามารถสร้างเมธอดในคลาสแม่เพื่อสร้างอินสแตนซ์ของคลาสย่อยโดยอิงจาก ModuleInventPurchSales และ enum StatusIssue ได้อย่างง่ายดาย แต่คุณจะต้องแก้ไขคลาสแม่ทุกครั้งที่เพิ่มคลาสย่อย ซึ่งไม่ใช่แนวคิดที่แท้จริงของการสืบทอดในการเขียนโปรแกรมเชิงวัตถุ ท้ายที่สุดแล้ว คุณไม่จำเป็นต้องแก้ไข RunBaseBatch หรือ SysOperationServiceBase ทุกครั้งที่เพิ่มงานแบตช์ใหม่
อีกทางเลือกหนึ่ง คุณสามารถใช้เฟรมเวิร์ก SysExtension ได้ ซึ่งจะต้องเพิ่มคลาสอีกหนึ่งคลาส โดยคลาสนี้จะต้องสืบทอดมาจาก SysAttribute คลาสนี้จะถูกใช้เป็นแอตทริบิวต์ที่คุณสามารถใช้ตกแต่งคลาสประมวลผลของคุณได้
คลาสนี้คล้ายคลึงกับวิธีการสร้างคลาสสัญญาข้อมูลสำหรับการใช้งาน SysOperation มาก กล่าวคือจะมีสมาชิกข้อมูลและเมธอดพารามิเตอร์สำหรับรับและกำหนดค่าเหล่านั้น
ในกรณีของเรา การประกาศคลาสอาจมีลักษณะดังนี้:
{
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) คุณสามารถเพิ่มเมธอด 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 ได้มากเท่าที่คุณต้องการ แต่สิ่งสำคัญที่ควรจำไว้คือ เมื่อเขียนเสร็จแล้ว คุณไม่จำเป็นต้องแก้ไขเมธอดนี้อีกเลย คุณสามารถเพิ่มคลาสย่อยลงในลำดับชั้นได้ และตราบใดที่คุณตกแต่งคลาสเหล่านั้นอย่างเหมาะสม เมธอด construct ก็จะค้นหาคลาสเหล่านั้นได้ แม้ว่าคลาสเหล่านั้นจะยังไม่มีอยู่เมื่อตอนที่เขียนเมธอดนี้ก็ตาม
แล้วเรื่องประสิทธิภาพล่ะ? ผมบอกตามตรงว่าผมยังไม่ได้ลองทดสอบประสิทธิภาพอย่างจริงจัง แต่โดยส่วนตัวแล้วผมคิดว่ามันน่าจะทำงานได้แย่กว่าการออกแบบคำสั่ง switch แบบดั้งเดิม อย่างไรก็ตาม เมื่อพิจารณาว่าปัญหาด้านประสิทธิภาพส่วนใหญ่ใน Dynamics AX เกิดจากการเข้าถึงฐานข้อมูล ผมจึงไม่ค่อยกังวลเรื่องนี้มากนัก
แน่นอนว่า หากคุณกำลังใช้งานอะไรบางอย่างที่ต้องสร้างอ็อบเจ็กต์จำนวนหลายพันชิ้นอย่างรวดเร็ว คุณอาจต้องตรวจสอบเพิ่มเติม แต่ในกรณีทั่วไปที่คุณเพียงแค่สร้างอ็อบเจ็กต์เดียวเพื่อประมวลผลเป็นเวลานาน ผมคิดว่ามันคงไม่สำคัญมากนัก นอกจากนี้ เมื่อพิจารณาจากคำแนะนำในการแก้ไขปัญหาของผม (ในย่อหน้าถัดไป) ดูเหมือนว่าเฟรมเวิร์ก SysExtension จะใช้การแคช ดังนั้นในระบบที่กำลังทำงานอยู่ ผมคิดว่ามันจะไม่ส่งผลกระทบต่อประสิทธิภาพอย่างมีนัยสำคัญ
การแก้ไขปัญหา: หากเมธอด construct ไม่พบคลาสย่อยของคุณ แม้ว่าคุณจะมั่นใจว่าได้ตกแต่งคลาสเหล่านั้นอย่างถูกต้องแล้ว อาจเป็นปัญหาเรื่องแคช ลองล้างแคชทั้งฝั่งไคลเอ็นต์และเซิร์ฟเวอร์ ไม่จำเป็นต้องรีสตาร์ท AOS แต่ก็อาจเป็นวิธีสุดท้ายที่ควรลอง
อ่านเพิ่มเติม
หากคุณชอบโพสต์นี้ คุณอาจชอบคำแนะนำเหล่านี้ด้วย:
- ลบนิติบุคคล (บัญชีบริษัท) ใน Dynamics AX 2012
- การเรียกบริการเอกสาร AIF โดยตรงจาก X++ ใน Dynamics AX 2012
- วิธีการทําซ้ําองค์ประกอบของ Enum จากรหัส X++ ใน Dynamics AX 2012
