Miklix

使用 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 可能看起来像这样:

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)中,您可以添加如下构造方法:

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上固定

米克尔·邦·克里斯滕森

关于作者

米克尔·邦·克里斯滕森
迈克尔 是 miklix.com 的创建者和所有者。他拥有 20 多年的专业计算机程序员/软件开发人员经验,目前全职受雇于一家大型欧洲 IT 公司。不写博客时,他把业余时间花在各种兴趣、爱好和活动上,这在一定程度上反映在本网站涵盖的各种主题上。