D365 : CoC Approach Extended

Hello guys.
My previous post was about the augmentation approach in D365. We will look a little bit deeper with this post.

To summarize; Augmentation is a way to enrich (or extend) the current solution without (or minimum) risk of allowing intrusive changes. Augmentation can be considered as a way of inheritance but there are some critical differences. It also can be compared with event handlers but easier to write.

A few words about the terminology; name of the whole concept is Augmentation. Creating an extension of a method can be called as wrapping or CoC.

Advantages of Augmentation;

  • Classes, tables, data entities or forms can be augmented.
    • For classes [ExtensionOf(classStr(ClassA))]
    • For tables or data entities [ExtensionOf(TableStr(TableA))]
    • For forms, you should use seperate augmentation classes for each design element
      • [ExtensionOf(formdatasourcestr(FormToExtend, DataSource1))]
      • [ExtensionOf(formdatafieldstr(FormToExtend, DataSource1, Field1))]
      • [ExtensionOf(formControlStr(FormToExtend, Button1))]
  • Public and protected methods of a class (table, data entity or forms) can be wrapped.
  • Public and protected variables are reachable.
  • Inherited classes (child classes) can also be augmented.
  • Sometimes you need to think about the inconsistency between inheritance and augmentation principles. There are solutions for it.
    • If there are methods that should never be augmented can be marked as [HookableAttribute(false)] or [Wrappable(false)].
    • A final method which is marked as [Wrappable(true)] can also be wrapped.
  • You can make the next call inside a try – catch block (added with PU 21)

Restrictions of CoC;

  • When wrapping a method which has parameters with a default value, wrapper method should not include or change that default parameter.
  • Characteristic properties of the method (signatures) cannot be edited. Access type (public or protected), parameter types, parameter list and return type must remain the same.
  • Augmentation classes can’t be augmented. Nested augmentation is not supported.

Restrictions of Next call;

  • Always call next unless the referred method explicitly marked as “Replaceable”.
  • next call must be done at the root level;
    • It can’t be controlled inside a conditional statement like if – else or switch – case blocks
    • It can’t be called inside loops like while, do – while or for loops.
    • Method can’t be prematured with an early return call.

Keep in mind that, most of the time intellisense support for CoC is not available yet. So you should use good – old copy paste technique until it will be available.

Ray (Emre) TUFEKCI

Advertisements

D365 : Open but protected source code – Class Extension Approach

Hello guys.

My previous two posts were related to two fundamental concepts of OOP and I also gave the examples about how to use and test them as simple as possible. I will assume that you have already seen those posts and continue to a new subject.

In this post, I will try to explain class extension approach of D365. This is not a common OOP principle but still should be considered inside OOP methodology. You may find similar approaches for other platforms or solutions. For D365 this is a way of “open but protected source code” solution.

Microsoft always offers a flexible and easy – to – implement ERP solution with Dynamics products. In terms of TCO (Total Cost of Ownership) long term usage costs is always a matter of discussion. Even license and consultancy costs are usually lower than the competitors, upgrade costs are always unpredictable and unmanageable. The flexibility of the product has a drawback of unnecessary and uncontrollable developments.

In the early releases of D365, overlayering (traditional way of development) and extension model (new way of development) were supported at the same time. But Microsoft had officially announced support for development with overlayering will not continue in the future. And Partners should have to move their solutions to the extension model. Extensions are easier to maintain and upgrade but flexibility is limited.

“Extension” term might be a little bit confusing because the term “extends” is previously used and well known concept of OOP and also exists in X++. Please see this post for inheritance in D365.

childclass

As you can easily say, to use additional functionality of the child class (ClassB) should be directly called (instantiated) in the code.

The new concept of extension in X++ is quite different. To differentiate the concept, it is mostly referred as Augmentation” or “Chain of Command (Coc)”.  If a ClassY augments the ClassX in ModelM, even no change is made on the calls for ClassX, additional features of ClassX which are provided by ClassY will be “effective” on the ModelM.

If this is still not complex enough for you, than you should try this one : ClassX can have multiple different augmentations on different models. “Effective” version of ClassX is determined by the model of the call.

In any given condition, a class always have a single “effective” version on a specific model. Not like classic extension model, augmentation of a class are never referenced directly by the code, only by the runtime system.

Let’s see an example about this approach. Before we see the example, I have to say that there is a naming restriction about augmentation classes. It has to have “_Extension” as a suffix and it has to be final. And you have to use [ExtensionOf(classStr(className))] command at the top of the class. Here it goes :


[ExtensionOf(classStr(ClassA))]
final class ClassA_Extension
{
}

You can try to remove final keyword and _Extension suffix to see the compiler errors for yourself. But please see the screen shot below. Instead of super(…) call, this methodology uses next(…) command and it is mandatory unless the referred method explicitly marked as “Replaceable“.

cocerror

Let’s write a couple of lines and see the difference.


[ExtensionOf(classStr(ClassA))]
final class ClassA_Extension
{
public str methodPublic()
{
str ret;

ret = next methodPublic();

ret = strFmt("This part comes from CoC, %1", ret);

return ret;
}

}

If there are more than one augmentation of the same class, this next(…) call will find and run the other augmentations. At the and of the chain, the original method is called. This will create a “Chain of Command (CoC)” which is also the name of this new concept in D365.

I have commented the call for ClassB at the runnable class the rest is the same. This means call for classA is not changed but result is different. This is the most critical part, please pay attention.

class RunnableClassForTest
{
    ///
    /// Runs the class with the specified arguments.
    /// 

    /// The specified arguments.
    public static void main(Args _args)
    {
        ClassA objectClassA = new ClassA();
        objectClassA.integerPublic = 1;
        objectClassA.setPrivateInteger(10);
        objectClassA.setProtectedInteger(100);
        info(objectClassA.methodPublic());

        //ClassB objectClassB = new ClassB();
        //objectClassB.integerPublic = 2;
        //objectClassB.setPrivateInteger(20);
        //objectClassB.setProtectedInteger(200);

        //info(objectClassB.methodPublic());
    }

}

Here goes the result.

cocresult

It might be a little bit confusing and hard to understand. But it is a really key feature of extensions.

Next post will be about CoC alone and it will be a little bit deeper.

Ray (Emre) TUFEKCI

 

OOP principles with X++ : Inheritance

Hello guys.
My previous post was about Encapsulation which is one of the fundamental principles of OOP. I have also created a class and show the way how to use (consume) that class. I will continue using that class and methodology, so please refer to this link to see the article if you can’t find the details that you need to.

Inheritance is another fundamental principle of OOP. It basically means, a class can be based on another class to extend its functionality. To learn more, please refer to this link. Most of the time, given examples about inheritance do not make so much sense. You have probably heard about vehicle types (vehicle, car, truck etc) or living things (creature, animals, human etc) .

I will try to show some real life examples from D365 FO to make the things clear. In the current application, the “SalesLineType” architecture controls many functions about a sales line.

saleslinetypes

There are several sales types that can be created in the application and we have a specific class for each type. Each sales line record should be managed by relevant class. If related sales line is an ordinary sales line record, SalesLineType_Sales is used. Or if it is a return sales order, SalesLineType_ReturnItem is used.

Some functions can be common for all of the sales line types. Some functions can be shared between multiple types, some additional functionality would be necessary for a single type and so on. The general approach is centralisation of the code whenever it is possible. With this approach, repeated code creation is prevented. Code is reusable, simpler and much more understandable.

For sales line type example, common functions which are applicable for every sales line should be placed at SalesLineType base class. Any additional functionality should be placed to the relevant child class.

Let’s look at SalesLineType.initFromSalesTable(…) method. It is a long method so I cut it from a random point.

void initFromSalesTable(SalesTable _salesTable, boolean _ignoreInventDim = false)
{
#ISOCountryRegionCodes
    //
    SalesTable_RU salesTableRU;
    boolean countryRegion_RU = SysCountryRegionCode::isLegalEntityInCountryRegion([#isoRU]);
    //
    //
    SalesLine_IN salesLine_IN;
    // 

    SalesLine_Intrastat salesLine_Intrastat;

    if (SysExtensionSerializerExtensionMap::isExtensionEnabled(tableNum(SalesLine_Intrastat)))
    {
        salesLine_Intrastat = salesLine.salesLine_Intrastat();
    }

    // .....

}

Now let’s see SalesLineType_ReturnItem.initFromSalesTable(…) method. Please pay attention to super(…) call. With this call, standard function from the SalesLineType will be executed. In some cases, super(…) call can be intentionally skipped. This is a rare condition which means standard functionality is invalid for the type.

public void initFromSalesTable(SalesTable _salesTable, boolean _ignoreInventDim = false)
{
    super(_salesTable, _ignoreInventDim);

    salesLine.ReturnStatus = ReturnStatusLine::Awaiting;
}

For the last example, let’s see SalesLineType_Journal.initFromSalesTable(…) method.

void initFromSalesTable(SalesTable _salesTable, boolean _ignoreInventDim = false)
{
    super(_salesTable, _ignoreInventDim);

    if (_salesTable.returnItem())
    {
        salesLine.ReturnStatus = ReturnStatusLine::Awaiting;
        salesLine.ReturnDeadline = _salesTable.ReturnDeadline;
    }
}

If you search for SalesLineType_Sales.initFromSalesTable(…) method, you would not be able locate it. This means standard functionality from SalesLineType class is enough for this sales type.

Let’s create our example for inheritance. We have already created ClassA. Let’s extend it with ClassB. Please notice the “extends” keyword.

class ClassB extends ClassA
{

}

If you right click and look for the possible overrides, you would notice that you can override public and protected methods. But you can’t override private methods.

overrides

Let’s make minor changes to a method.

class ClassB extends ClassA
{
    ///
    ///
    /// 

    ///
    public str methodPublic()
    {
        str ret;
        ret = super();
        ret = strFmt("Welcome ClassB : %1", ret);
        return ret;
     }
}

Create a runnable class for test and run. Please notice the second block for child class.

class RunnableClassForTest
{
    ///
    /// Runs the class with the specified arguments.
    /// 

    /// The specified arguments.
    public static void main(Args _args)
    {
        ClassA objectClassA = new ClassA();
        objectClassA.integerPublic = 1;
        objectClassA.setPrivateInteger(10);
        objectClassA.setProtectedInteger(100);
        info(objectClassA.methodPublic());

        ClassB objectClassB = new ClassB();
        objectClassB.integerPublic = 2;
        objectClassB.setPrivateInteger(20);
        objectClassB.setProtectedInteger(200);

        info(objectClassB.methodPublic());
    }

}

Here goes the result.

inheritedresult

Please create your own example and try whatever you want. You can override methods, add new ones, skip the call for super(), call the super() twice etc. This will help you to understand the concept.

Ray (Emre) TUFEKCI

OOP principles with X++ : Encapsulation

Hello guys.
As you all know X++ was (and is) an Object Oriented Programming Language which evolves with each new version. Some features are the same but some has updated corresponding to Visual Studio environment.

Let’s keep it simple and start from the beginning. I can’t point each and every difference so please make your own comparison with your previous knowledge base.

This article will be about Encapsulation with X++ which is a core element of OOP. If you don’t know what encapsulation is, please refer to this article.

Let’s consider we have a base class (ClassA) which has both public, protected and private variables and also have public, protected and private methods.

We will use the same class in the future for the other concepts. If you don’t know where to start or how to create a new model, project or a class, please refer to this article.

class ClassA
{
    public int integerPublic;
    private int	integerPrivate;
    protected int integerProtected;

    public str methodPublic()
    {
        str retVal;
        retVal = strFmt("Values from public method are : %1 - %2 - %3", 
                    this.integerPublic, 
                    this.integerPrivate, 
                    this.integerProtected);
        return retVal;
    }

    private str methodPrivate()
    {
        str retVal;
        retVal = strFmt("Values from private method are : %1 - %2 - %3", 
                    this.integerPublic, 
                    this.integerPrivate, 
                    this.integerProtected);
        return retVal;
    }

    protected str methodProtected()
    {
        str retVal;
        retVal = strFmt("Values from protected are : %1 - %2 - %3", 
                    this.integerPublic, 
                    this.integerPrivate, 
                    this.integerProtected);
        return retVal;
    }

    public void setPrivateInteger(int _integerPrivate)
    {
        integerPrivate = _integerPrivate;
    }

    public int getPrivateInteger()
    {
        return integerPrivate;
    }

    public void setProtectedInteger(int _integerProtected)
    {
        integerProtected = _integerProtected;
    }

    public int getProtectedInteger()
    {
        return integerProtected;
    }
}

Now let’s use the class with a runnable class. If you don’t know how to do it, please refer to this article.

class RunnableClassForTest
{
    public static void main(Args _args)
    {
        ClassA objectClassA = new ClassA();
        //public variables can be accessed directly
        objectClassA.integerPublic = 1;

        //private and protected variables can only be accessed by member methods
        objectClassA.setPrivateInteger(10);
        objectClassA.setProtectedInteger(100);

        info(objectClassA.methodPublic());
        //Private and protected methods can't be called outside the class
        //info(objectClassA.methodPrivate());
        //info(objectClassA.methodProtected());

        //we can only access the private and protected variables if there are member methods to do so
        info(strFmt("%1 - %2", 
            objectClassA.getPrivateInteger(), 
            objectClassA.getProtectedInteger()));
     }
}

When you hit Ctrl + F5 (Start Without Debugging) the result should look like this:


Next article will be about inheritance. And we will create a new class; ClassB.

Ray (Emre) TUFEKCI

D365 FO Development Basics : Hello world

Hello guys.

If you are learning a new development language, you should always salute the world. This is very important tradition, something like sending energy to the universe. 🙂

Actually it is a very common way to understand the concept. So let’s do it.

  1. First things first, you should have a running environment for this purpose. If you don’t have such environment, you may try to download with these articles.
  2. If this is your first time, you should create a new model. You can do this under Dynamics 365 menu.
    createmodelIf this is for just learning, you can name the model whatever you wanted. But in the real development cycles, you should follow naming conventions.
    createmodel2Keep it simple; create a new one.
    createmodel3Only 3 models, Application Foundation, Application Platform and Application Suite are more than enough for now. If you need more models to be referenced, you can always edit these references.
    createmodel4Just a summary, click Finish.
    createmodel5
  3. After creation of the model, wizard asks to create a project. If you already have a model, you can always create a new project. You should use Dynamics 365 Unified Operations.
    createmodel6
  4. Go to solution explorer, right click and select Add new item. If you can’t see the solution explorer, you can find it under View menu. addnewitem
    Select code section on the left hand side menu and select runnable class from the right hand side window.
    addnewitem2
  5. Just add some code here. Don’t forget to salute the world.
    basicclass
  6. Before you test it, you should mark this class as a startup project
    setasstartupprj
  7. And here goes the result. Outstanding.
    helloworld

Ray (Emre) TUFEKCI

D365 vs AX2012 : Major changes on Server Architecture

Hello and wish a happy and healthy new year to everyone.

If you are working with D365 \ AX for a while you may probably thinking some of the technology should be considered or some parts of the framework should be replaced. Lucky you, there are some people thinking the same way with you at Microsoft. Let`s see some of the major changes on server architecture.

  • Maybe most developers have already realised that Ax2012 AOS was acting like a web service endpoint. This feature was handy but not common used. There was data contracts like exchange rates, customer lists etc and metadata contracts like EDT or Enum lists. With D365 this feature is extended and become the default way of communication. The services endpoint on the server is now responsible for returning all form and control metadata and data to the browser-based client.
  • Related to the previous item, RPC (remote procedure call) to server is obsolete and removed. The form objects still run on the server, and rendering has been optimised for browsers and other clients through server and client-side (browser) investments.
  • Hence D365 is a web based solution, it is deployed to an Internet Information Services (IIS) web application. In the Azure, it is deployed to Microsoft Azure infrastructure as a service (IaaS) virtual machines (VMs). This means D365 is no longer a Windows Service.
  • On Azure, users have to use Azure Active Directory Account for login. This topic is a little bit beyond my knowledge base, you should make a search if you need more information.
  • On security side, previous versions were merging the permissions. Where ever a call was made, all related permissions were merged and highest level permission is granted. For example; if a data source is granted read permissions through an entry point and edit permissions through a form, the user has edit permissions to that data source. But In D365, permissions through the entry point defines the final permission level.
  • With the Visual Studio based development model, metadata subsystem is totally changed. Hence there is no need to support older versions and MorphX based development model, new metadata system is more effective. It is categorised by models and you don’t have to compile all models every time. This approach dramatically shortens the compile time.

 

Ray (Emre) TUFEKCI

Alternative way to download D365 FO evaluation copies

In the previous article, we discussed how to download D365 FO for demo and testing purposes. Please click here to see the article.

If you don’t have neither partner source nor customer source access, there is still an alternative way to have it.

Thanks to my dear friend Metin Emre from Turkey who shared this article with me. Please click this link to see the details.

Ray (Emre) TUFEKCI