Extensible X++: Chain of Command


As you can see on the Dynamics Roadmap a new capability is being introduced in X++; it enables strongly typed extension capabilities of public and protected methods; including access to public and protected members.

Oh; I almost forgot: This is my new favorite X++ feature.

See this video to learn more.

THIS POST IS PROVIDED AS-IS; AND CONFERS NO RIGHTS.

Comments (32)

  1. Ankit7878 says:

    I have written some custom code in two places of standard class CustVendSumForPaym method run(). Now I am trying to move the code to a extension class but if I move the code it would break the proper code execution sequence.
    Is it possible to use chain of command in this scenario?

    1. Chain of command enables you to write logic pre and post a method call. Much like the pre and post events. From your description it seems you need to inject logic “in the middle” of existing code – here chain of command will not help until the method is broken down to smaller methods. I suggest you log an extensibility request. See here how: https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/extensibility/extensibility-requests

  2. krishnendu says:

    I have written some custom public methods in one of the standard table say bankaccount table method1.
    I am moving the method to a extension class in the same package different model without any next keyword it works fine. but if I move the method to a new extension class in a new package which has referenced that model I am still getting the error for chain of command.

    Am I missing something?

    1. Hi krishnendu,
      The validation for next has nothing todo with where the code lives. You can use chain of command to wrap methods on classes in the same model if you like.
      So yes, I think you miss something, but I’m not quite sure what. Can you write up some repro steps so we can try? Feel free to contact me through the contact button to the right.
      Thanks,
      Michael

      1. krishnendu says:

        Hi MFP,

        I the only thing which I did is moved the extension class to a new package and model and then it gives us the error “Chain of command method must contain one next call”

      2. krishnendu says:

        Hi MFP,

        After full build the error went off, thanks for ur prompt response.

      3. krishnendu says:

        Hi MFP,
        If I have customized a standard table method and I want to execute that method in a pre/post event way how can we achieve that
        in COC. The next keyword has to be after/before our method logic in the extension class?

  3. Marius says:

    Hi Michael,
    This is great feature, and I like possibility to access protected class fields. But why private methods are left out?
    I have real life example I have to deal daily: adding extra field to report (let’s say SalesConfirmation, the rest is pretty much similar). Normal process would be Extending SalesConfirmDetailsTmp table adding new field X, and using this X in report design. But the biggest problem comes when X is calculated based on data from CustConfirmTrans. The only place where SalesConfirmDetailsTmp fields are initialized is in SalesConfirmDP.setSalesConfirmDetailsTmp method, which is private. So simple task adding extra field with extensions becomes impossible without overlay. It’s the same story with nearly all reports *DP classes, and it’s a shame, to have to overlay application just for that.

    1. Thanks Marius,
      Declaring methods and members as private/protected/public is how encapsulation is controlled in X++. Other object oriented languages have similar (if not identical) behavior. By declaring methods and members as private the code author can shield/hide the consumer from details the consumer shouldn’t know about – this benefits both author and consumer: The consumer will have a more concise API, the author will have a level of freedom for refactoring code without breaking consumers. Had we enabled CoC to access private methods then we would effectively have broken encapsulation in X++. That is not desirable.

      Obviously; we must support the scenario you describe. The main reason why sealing is not enforced immediately is to allow time to discover and report and correct the areas that are not extensible yet. I’d encourage you (and anyone else) to log extensibility requests when places are discovered that cannot be extended for your need. See how here: https://docs.microsoft.com/en-us/dynamics365/unified-operations/dev-itpro/extensibility/extensibility-requests?toc=dynamics365/unified-operations/fin-and-ops/toc.json

      I’m also happy to let you know that the SalesConfirmDP class has already been reported and made extensible. The updated version will be available with the “Fall 2017” release (and CTPs of same).

      Happy extending,
      Michael

      1. Marius says:

        Hi Michael,

        Thanks for head up about DP classes. I tried a few chain of command cases and noticed that I can access only class fields declared only in immediate parent class. For example if class B extends class A and I create extension class B_Extension, in protected (where chain of command is used) methods I can access protected fields only from B, but not from A. Is that done intentionally?
        I know it’s possible to expose protected field by extending A, but I would expect protected fields are visible from all ancestors.
        Thanks

      2. Marius says:

        Sorry, my mistake. It is possible to access protected fields from all ancestors.
        Regards

  4. Alejandro Parodi says:

    Hello Michael. Are there any plans in the future to allow us to avoid the execution of standard code? We faced some situations where we need bypass the execution of a method without canceling the whole transaction.

    1. Hi Alejandro,
      If you consider the consequences of allowing to skip the standard code (i.e. not calling next), I expect you’ll reach the same conclusion as us: It isn’t desirable.
      Overlayering gives you the ability to replace any method. You take a copy of the original method and can edit it as you please. There is some tooling on top that gives a better experience, but at the end of the day customizations done this way are intrusive, and cannot be upgraded automatically. (For example, when the original code is changed, those changes must be merged in.)
      If CoC allowed skipping the execution of standard code, you could take a copy of the standard code, edit it in any way you want, place it in a wrapper method and voila – you had overlayering in disguise. If we allowed this, we would be back to square one. CoC would have the exact same replacing semantics and problems as overlayering – just without the tooling.

      Naturally; there will be cases where replacing-semantics are required and acceptable. The application code must be (re)written to support this on a case-per-case basis.

      Thanks,
      Michael

  5. Eddy says:

    Great news!! When will be available this awesome feature? (from August i hope)

    1. The feature is available in Platform Update 9. It is available now on LCS.

  6. Jens says:

    Hi Michael,

    this looks really awesome! Is there any logic behind how the execution order in the chain is determined? If in your example ‘b)’ in a second solution somebody would implement the post method as well containing a dialog box (e.g. ‘Posting journal, continue?’), might this potentially break the application since your post wrapper might be called first leaving the second one with a dialog in a transaction?

    Kind regards

    1. Thanks for the positive feedback Jens!

      Technically, there is logic behind the execution order – the order will be deterministic. I agree we can construct examples with mutual exclusive extensions. Practically, I think the problem is manageable. Consider this:
      1. Even today “one bad apple” can spoil the solution. For example an unconditional exception in a pre/post event handler breaks everything. No one is interested in this of course, so we are all trying to be well-behaved. (Including not starting a dialog in a post method).
      2. In most cases multiple CoC extensions will live seamlessly side-by-side. However; there will be cases where the extensions are incompatible. Similarly, we support changing labels on EDTs as an extension. Only one label can win – if two solutions change the same label to something different, they are likely functionally incompatible. Example: If one solution changes “Customer name” to “Client name” and another solution changes “Customer name” to “Patient name” – they will collide, and not just on the label.
      3. We have a great last defense: Validation! Any system (including all extensions) must be thoroughly tested before go live. If any extensions are incompatible, it will surface, allowing the partner and the customer to resolve the incompatibilities in due time.

      We’ve recently published a lot of new Extension documentation. I’d suggest reading these two related topics: Migrate from overlayering to extensions
      Intrusive customizations.

      Happy extending,
      Michael

      1. David says:

        Hi Michael,

        I like the concept, it will help a lot, but one more question about the execution order:
        Will it be deterministic accross deployments with the same codebase? (Test environments/production). Simple example with an other extension on the calcAmount method where the extension multiples by 2 for example. The result always will be 284 or always 184?

        Will the order of the first two extension be the same if a third comes in? (3 extension, 3 vendors, same layer for example)

        Thanks,

      2. David about your question:
        “Will it be deterministic across deployments with the same codebase?”

        No it is not deterministic which CoC method in the chain is going to be first, second, etc. The only thing that is deterministic is the method base implementation (the original implementation you are wrapping or chaining) is the last one in the execution in the chain.

  7. Evgeniy says:

    Hi Michael. I have one question – how can we use this new feature if we need to add a new Inventory Dimension field in InventDim table? The problem is that in case of creation of new InventDim record the method looks like:
    ttsbegin;
    inventDim.clear();

    inventDim.insert();
    ttscommit;

    And how can we make our changes in existing transaction (fill our new inventory dimension field)? Because your new approach adds our new transaction OVER existing transaction. But how can we change existing transactions? In this case we will have wrong record in database, because insert is already called in source transaction. And we need to run our new custom code (fill new inventDim field) in existent transaction.

    Thanky you in advance.

    1. Hi Evgeniy,
      This feature is not enabling arbitrary extension points in arbitrary methods – only pre- and post. So this will not help in your example, where you are looking for an extension point between clear() and insert().
      Thanks,
      Michael

  8. MehrdadG says:

    Hi Michael,

    Some questions about this wonderful feature:

    – Is there already some information available about the algorithm that defines the order of the calls? Or it is the same as event handlers without any guarantee of the call/execution order ?
    – Can we access the list of methods that are in the list of same the chain of command? What about accessing their return value? Does the framework provides a way to be able to see what each individual method has returned?
    – Does this also supports recursive calls?
    – What about static methods? Is it also supported for static methods? (i.e. construct methods)
    – Is it also supported for X++ methods on the “Form” level?

    In the end, would like to thank you for sharing as always!

    1. Hi MehrdadG,
      By-design you cannot make informed decisions about the presence of other extension methods. Extensions must be self-contained and well-behaved – they shouldn’t care or need to care about other extensions.

      More details about where the feature applies will be shared once we get closer availability. For now I can confirm that public and protected instance methods on classes and tables are supported.

      Michael

  9. marcel says:

    cool feature, thanks!
    So the next next question is: when will X++ support generics 🙂

  10. Bostjan says:

    So the next question is: when will X++ support function overloading so that the point Denis brings up won’t be an issue anymore 🙂

    Boštjan

    1. There are many obstacles to implement overloading in X++.
      One of them was the X++ pcode interpreter – that one is now gone. Now we use the CLR – which supports method overloading.
      The next one is overlayering; or rather the meta model required to support overlayering. Today it is based on names, which means names must be unique. Once we can kill overlayering support, we’ll be one step closer.
      We are slowly getting there – but don’t hold your breath.

  11. Denis says:

    how do you plan handle default parameters in extension method? should extension method signature be the same as original method or default parameters can be optionally skipped

    1. Hi Denis,

      Thanks for your interest in this. The methods participating in a chain-of-command are invoked by framework. The framework wouldn’t know how to provide special parameter values for the individual chain-of-command methods. In other words: The signatures must be identical – no parameter inconsistencies are accepted.

      That said, you can probably already achieve what you need. A method’s signature is a contract between a the method and the caller. Adding a new optional parameter to an existing method only makes sense if both sides of the contract uptakes the new parameter. In an extension model you cannot change existing logic to provide the extra parameter – that would require overlayering. However; you can call the method with the extra parameter from your own logic. If that is the requirement, then it is already supported. Here is how:
      1. Add a new unique method to the class as an extension. See how here: https://blogs.msdn.microsoft.com/mfp/2015/12/15/x-in-ax7-extension-methods/
      2. Do what you need with the extra parameter, and then call the original method with the original parameters. (Similar to calling super() or next)
      3. Use your new method with the extra parameters from your own logic.

      1. Denis says:

        Thanks for the reply, but my question was about the following situation(based on your example – lets assume JournalPosting is in applicationSuite that is managed by Microsoft)
        if Microsoft in new update adds second parameter with default value to post(so the new method signature for example will become
        void post(TransDate _date, Tax _taxCalc = null)
        { .. }
        Does that means that your extension code will stop compiling and require code upgrade?
        If yes, it is very strange feature, as the original purpose of extensions was to prevent code upgrade step

      2. If a method participating in chain-of-command has less parameters than the original code, how will the framework be able to pass the parameters through the chain? So also here the method signatures must align.

        The main purpose of the push to extensibility is to support seamless upgrade. This is a two step process: 1. Eradicate intrusive customizations and 2. Be backward compatible. This feature is part of step 1 – it enables migrating many intrusive customizations into extensions. Second step is to stop making breaking change. Adding a parameter (optional or not) to a non-final method is a breaking change; even today this action will break inherited classes that has overridden the method. The chain-of-command feature is not adding any extra requirements for being backwards compatible; i.e. changes breaking chain-of-command will also break normal inherited classes.

      3. Denis says:

        Do you have any view why next is made as a mandatory in extension? if there any chance that this will be changed to optional? I agree it should be called in most cases, but a case that we have in our solution in your example will look like the following:
        -in original calc method there is a throw error if the journalType is Inventory
        -we want to extend the solution by removing this throw in certain cases and implement our own calculation. We want to prevent original code(with the throw from execution as it does not support(and doesn’t not make sence for Inventory journalType)

      4. If next is not mandatory – then chain-of-command is conceptual identical to overlayering (without all the tooling). It would allow any one to replace literally any piece of code with their own implementation. Given the goal is to ensure seamless upgrades then we cannot allow a generic extension capability offering replacement semantics.

        I agree, there are cases where it would make sense to not call next – for those we’ll need to refactor the code to support the extensions.

Skip to main content