使用 SysExtension 框架找出 Dynamics AX 2012 中要实例化的子类
已出版: 2025年2月16日 UTC 00:26:04
本文介绍如何使用 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,但这可能是最后的手段。