Miklix

使用 SysExtension 框架找出 Dynamics AX 2012 中要實例化的子類

已發佈: 2025年2月16日 凌晨12:26:05 [UTC]
最後更新: 2026年1月12日 上午8:43:23 [UTC]

本文介紹如何在 Dynamics AX 2012 和 Dynamics 365 for Operations 中使用鮮為人知的 SysExtension 框架,根據屬性裝飾實例化子類,從而實現易於擴展的處理類層次結構設計。


該頁面是由英語機器翻譯而來的,以便盡可能多的人可以訪問。不幸的是,機器翻譯還不是一項完善的技術,因此可能會出現錯誤。如果您願意,可以在這裡查看原始英文版本:

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、PurchLine 或兩者都無關。這樣看來,就已經有很多不同的組合要考慮了。

假設你知道目前你只需要處理少數幾種組合,但你也知道隨著時間的推移,你會被要求處理越來越多的組合。

我們先簡單一點,假設目前你只需要處理與 SalesLine 相關的、狀態為 ReservPhysical 或 ReservOrdered 的記錄,其他所有組合暫時都可以忽略,但既然你知道以後必須處理它們,那麼你就需要設計一個易於擴展的程式碼。

目前你的組織架構可能如下圖所示:

  • MyProcessorMyProcessor_SalesMyProcessor_Sales_ReservOrderedMyProcessor_Sales_ReservPhysical

現在,您可以輕鬆地在父類別中實作一個方法,該方法基於 ModuleInventPurchSales 和 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;
}

你也應該為每個資料成員實作一個參數方法,但我在這裡省略了這些步驟,因為我相信你知道怎麼做-否則,我們就把它當作一個練習吧 ;-)

現在,您可以使用屬性類別來修飾每個處理類別。例如,類別聲明可以如下所示:

[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)中,你可以加入一個如下所示的建構方法:

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;
}

真正有趣的部分——也是本文的重點(請原諒我用了這個雙關語)——是 SysExtensionAppClassFactory 類別中的 getClassFromSysAttribute() 方法。這個方法接受一個繼承層次結構的父類別名稱(這個父類別不需要位於層次結構的頂端;它只是意味著只有繼承自該父類別的類別才符合條件)和一個屬性物件。

然後它傳回一個繼承自指定超類別並用對應屬性修飾的類別的物件。

當然,你可以根據需要向構造方法添加更多驗證或邏輯,但關鍵在於,一旦實現,你就不需要再修改此方法。你可以在繼承層次結構中加入子類,只要確保正確修飾它們,建構方法就能找到它們,即使它們在編寫時並不存在。

性能方面如何?說實話,我還沒做過基準測試,但我的直覺是,它的效能可能不如傳統的 switch 語句設計。不過,考慮到 Dynamics AX 中絕大多數的效能問題都源自於資料庫訪問,我覺得不必太擔心。

當然,如果您要實現的功能需要快速建立數千個對象,那麼您可能需要進一步調查。但在通常情況下,您只需要實例化一個物件來進行一些耗時的處理,我認為這不會造成什麼影響。此外,考慮到我之前的故障排除建議(下一段),SysExtension 框架似乎依賴於緩存,因此在運行的系統中,我懷疑它不會造成明顯的效能損失。

故障排除:如果建構方法找不到您的子類,即使您確定它們已正確裝飾,也可能是快取問題。請嘗試清除客戶端和服務端的快取。通常不需要重新啟動 AOS,但這可能是最後的手段。

進一步閱讀

如果您喜歡這篇文章,您可能也會喜歡這些建議:


分享至 Bluesky在 Facebook 分享在 LinkedIn 分享在 Tumblr 上分享分享至 X在 LinkedIn 分享固定在 Pinterest 上

Mikkel Christensen

關於作者

Mikkel Christensen
麥可 是 miklix.com 的創建者和所有者。他有超過 20 年的專業電腦程式設計師/軟體開發人員經驗,目前全職受僱於一家歐洲大型 IT 公司。不寫部落格時,他會將業餘時間花在各種各樣的興趣、愛好和活動上,這在一定程度上反映在本網站所涵蓋的主題的多樣性上。