Miklix

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

出版された: 2025年2月16日 0:25:48 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 のどちらにも関連しているかどうかによって異なります。すでに、さまざまな組み合わせが考えられます。

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

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

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

  • マイプロセッサ
    • マイプロセッサ_セールス
      • MyProcessor_Sales_ReservOrdered
      • MyProcessor_Sales_ReservPhysical

これで、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企業に常勤している。ブログを書いていないときは、さまざまな興味、趣味、活動に余暇を費やしている。