Miklix

SysExtension フレームワークを使用して Dynamics AX 2012 でインスタンス化するサブクラスを見つける

出版された: 2025年2月16日 0:25:48 UTC
最終更新日 2026年1月12日 8:43:04 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 フレームワークを使用するという、はるかにエレガントな、しかし残念ながらあまり知られていない方法があります。

このフレームワークは、サブクラスを装飾するための属性を活用することで、どのサブクラスを何の処理に使用すべきかをシステムが判断できるようにします。constructメソッドは依然として必要ですが、正しく実装すれば、新しいサブクラスを追加する際に変更する必要がなくなります。

架空の例として、InventTransテーブルに基づいて何らかの処理を行う階層を実装するとします。どの処理を行うかは、レコードのStatusReceiptとStatusIssue、そしてレコードがSalesLine、PurchLineのいずれにも関連しているか、あるいはどちらにも関連していないかによって異なります。既に、様々な組み合わせを検討していることになります。

現時点では少数の組み合わせのみを処理すればよいとわかっていますが、時間の経過とともにさらに多くの組み合わせを処理できるようになることもわかっているとします。

ここでは比較的単純にして、StatusIssue が ReservPhysical または ReservOrdered である SalesLine に関連するレコードのみを処理する必要があるものとします。その他のすべての組み合わせは今のところ無視できますが、後で処理する必要があることがわかっているため、簡単に拡張できるようなコードを設計する必要があります。

現時点での階層は次のようになります。

  • マイプロセッサマイプロセッサ_売上マイプロセッサ_売上予約注文マイプロセッサ_売上予約物理

さて、スーパークラスに、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() メソッドです。このメソッドは、階層構造のスーパークラス名(このスーパークラスは階層の最上位にある必要はありません。単に、このクラスを拡張するクラスのみが対象になるという意味です)と属性オブジェクトを受け取ります。

次に、指定されたスーパークラスを拡張し、対応する属性で装飾されたクラスのオブジェクトを返します。

もちろん、construct メソッドには必要に応じてバリデーションやロジックを追加できますが、ここで重要なのは、一度実装すれば、このメソッドを再度変更する必要がなくなるということです。階層構造にサブクラスを追加することも可能です。適切なデコレーションを施しておけば、construct メソッドは、たとえサブクラスが実装時に存在していなくても、そのサブクラスを見つけ出します。

パフォーマンスはどうでしょうか?正直なところ、ベンチマークは試していませんが、直感的には従来のswitch文の設計よりもパフォーマンスが劣るのではないかと思います。しかし、Dynamics AXにおけるパフォーマンスの問題の大部分はデータベースアクセスに起因することを考えると、それほど心配する必要はないでしょう。

もちろん、数千ものオブジェクトを迅速に作成する必要がある実装の場合は、さらに詳しく調査する必要があるかもしれません。しかし、単一のオブジェクトをインスタンス化して時間のかかる処理を行うような典型的なケースでは、それほど問題にはならないと思います。また、トラブルシューティングのヒント(次の段落)を考慮すると、SysExtensionフレームワークはキャッシュに依存しているように見えるため、稼働中のシステムではパフォーマンスに大きな影響が出るとは考えにくいです。

トラブルシューティング:サブクラスが正しくデコレートされているにもかかわらず、construct メソッドがサブクラスを見つけられない場合は、キャッシュの問題である可能性があります。クライアントとサーバーの両方でキャッシュをクリアしてみてください。AOS を実際に再起動する必要はありませんが、最後の手段として検討してください。

さらに読む

この投稿が気に入った場合は、次の提案も気に入るかもしれません:


BlueskyでシェアFacebookでシェアLinkedInでシェアTumblrでシェアXでシェアLinkedInでシェアPinterest にピン留めする

ミケル・クリステンセン

著者について

ミケル・クリステンセン
ミッケルはmiklix.comの開発者でありオーナーです。プロのコンピューター・プログラマー/ソフトウェア開発者として20年以上の経験を持ち、現在はヨーロッパの大手IT企業に常勤している。ブログを書いていないときは、さまざまな興味、趣味、活動に余暇を費やしている。