Miklix

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

已出版: 2025年2月16日 UTC 00:26:04
最后更新 2026年1月12日 UTC 08:43:23

本文介绍了如何在 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 公司。不写博客时,他把业余时间花在各种兴趣、爱好和活动上,这在一定程度上反映在本网站涵盖的各种主题上。