Miklix

การใช้เฟรมเวิร์ก 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 มาก กล่าวคือจะมีสมาชิกข้อมูลและเมธอดพารามิเตอร์สำหรับรับและกำหนดค่าเหล่านั้น

ในกรณีของเรา การประกาศคลาสอาจมีลักษณะดังนี้:

class MyProcessorSystemAttribute extends SysAttribute
{
    ModuleInventPurchSales  module;
    StatusIssue             statusIssue;
    StatusReceipt           statusReceipt
}

คุณต้องสร้างเมธอด `new()` สำหรับสร้างอินสแตนซ์ของสมาชิกข้อมูลทั้งหมด หากต้องการ คุณสามารถกำหนดค่าเริ่มต้นให้กับบางส่วนหรือทั้งหมดได้ แต่ฉันไม่ได้ทำเช่นนั้น

public void new(ModuleInventPurchSales  _module,
                StatusIssue             _statusIssue,
                StatusReceipt           _statusReceipt)
{
    ;

    super();

    module          = _module;
    statusIssue     = _statusIssue;
    statusReceipt   = _statusReceipt;
}

นอกจากนี้ คุณควรสร้างเมธอด parm สำหรับสมาชิกข้อมูลแต่ละตัวด้วย แต่ผมได้ละเว้นส่วนนั้นไว้ในที่นี้ เพราะผมมั่นใจว่าคุณรู้วิธีทำอยู่แล้ว — หรือถ้าไม่รู้ ก็ถือว่าเป็นแบบฝึกหัดแล้วกัน ;-)

ตอนนี้คุณสามารถใช้คลาสแอตทริบิวต์ของคุณเพื่อตกแต่งคลาสประมวลผลแต่ละคลาสได้แล้ว ตัวอย่างเช่น การประกาศคลาสอาจมีลักษณะดังนี้:

[MyProcessorSystemAttribute(ModuleInventPurchSales::Sales,
                            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 ได้ดังนี้:

public static MyProcessor construct(ModuleInventPurchSales _module,
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 แต่ก็อาจเป็นวิธีสุดท้ายที่ควรลอง

อ่านเพิ่มเติม

หากคุณชอบโพสต์นี้ คุณอาจชอบคำแนะนำเหล่านี้ด้วย:


แชร์บนบลูสกายแชร์บนเฟสบุ๊คแชร์บน LinkedInแชร์บน Tumblrแชร์บน Xแชร์บน LinkedInปักหมุดบน Pinterest

มิคเคล คริสเตนเซ่น

เกี่ยวกับผู้เขียน

มิคเคล คริสเตนเซ่น
ไมเคิล คือผู้สร้างและเจ้าของเว็บไซต์ miklix.com เขามีประสบการณ์เป็นโปรแกรมเมอร์/นักพัฒนาซอฟต์แวร์คอมพิวเตอร์มืออาชีพมากว่า 20 ปี และปัจจุบันทำงานเต็มเวลาให้กับบริษัทไอทีขนาดใหญ่แห่งหนึ่งในยุโรป เมื่อไม่ได้เขียนบล็อก เขาจะใช้เวลาว่างไปกับความสนใจ งานอดิเรก และกิจกรรมต่างๆ มากมาย ซึ่งในระดับหนึ่งอาจสะท้อนให้เห็นได้จากหัวข้อต่างๆ มากมายที่กล่าวถึงในเว็บไซต์นี้