使用 SysExtension 框架找出 Dynamics AX 2012 中要實例化的子類
已發佈: 2025年2月16日 凌晨12:26:05 [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 相關的、StatusIssue 為 ReservPhysical 或 ReservOrdered 的記錄,其他所有組合暫時都可以忽略,但因為您知道以後必須處理它們,所以您需要以一種易於擴展的方式設計您的程式碼。
你的層次結構現在可能看起來像這樣:
- 我的處理器
- 我的處理器銷售
- 我的處理器銷售預訂
- 我的處理器_銷售_保留物理
- 我的處理器銷售
現在,您可以輕鬆地在超類別中實作一個方法,該方法基於 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;
}
真正有趣的部分 - 並且實際上是整篇文章的物件(原諒這個雙關語) - 是 SysExtensionAppClassFactory 類別中的 getClassFromSysAttribute() 方法。此方法的作用是接受層次結構的超類別的名稱(而這個超類別不必位於層次結構的頂部;它只是意味著只有擴展此類別的類別才有資格)和一個屬性物件。
然後,它傳回一個擴展指定超類別並用相應屬性修飾的類別的物件。
顯然,您可以根據需要向構造方法添加盡可能多的進一步驗證或邏輯,但這裡重要的一點是,一旦實現,您就永遠不必再修改此方法。您可以將子類別添加到層次結構中,只要您確保適當地修飾它們,構造方法就會找到它們,即使在編寫時它們並不存在。
那麼性能怎麼樣呢?說實話,我還沒有嘗試對它進行基準測試,但我的直覺是,它的表現可能比經典的 switch 語句設計更差。不過,考慮到 Dynamics AX 中迄今為止大多數效能問題都是由資料庫存取引起的,我不會太擔心這一點。
當然,如果您正在實現需要快速創建數千個物件的東西,您可能需要進一步調查,但在典型情況下,您只需要實例化一個物件來執行一些漫長的處理,我懷疑這是否重要。此外,考慮到我的故障排除提示(下一段),看來 SysExtension 框架依賴於緩存,因此在運行系統中,我懷疑它是否會對性能造成重大影響。嘗試清除客戶端和伺服器上的快取。實際上沒有必要重新啟動 AOS,但這可能是最後的手段。