Skip to content

Merging concurrent changes in AX with TFS

Suppose you’re an AX developer working in an AX instance shared by few other developers. You want to do some changes that would likely cause issues to your colleagues until they’re finished, therefore you’ve created a separate instance and work there. To simplify the example, assume that you’ve changed only a single method.

If nobody changed it in the meantime, it’s all good – you can simply import the method. But if somebody changed it, importing your code is a bad thing, because you would overwrite somebody else’s code. It doesn’t mean just that a feature would disappear; you would probably remove only a part of a feature and the behavior of the remaining application would be unpredictable.

What you have to do is to merge your changes with the current version of the application. In the simplest case, you want to keep all changes, therefore you’ll add your changes to what’s already there. Sometimes you want to replace a previous change (because, for example, somebody made a quick fix before your more robust solution gets ready). Sometimes you’ll have to implement completely new code to allow concurrent changes to work together. In the worst case, the changes are not compatible and can’t be merged together at all. (This usually reveals a problem with project management.)

My experience indicate that the main problem is not that developers fail to merge code correctly – they often don’t do it at all and overwrite previous changes. Unfortunately you can’t avoid merging if you want to support parallel development on the same objects, nevertheless you CAN ensure that people don’t skip merging.

The answer is obviously a version control system. If you take an object from a repository, modify it and you’re going to commit it, the version control system checks whether there aren’t already conflicting changes. If there are any, you’re not allowed to continue without resolving conflicts.

Let’s take a look at a concrete example with a few pictures. I’m going to simulate two developers working on AX2012 with Team Foundation Server. Each developer has his own AX instance pointing to the same team project and branch.

To make it easier, I used a single instance and I simulated the other developer by adding some change directly in Visual Studio, using a different TFS workspace. I took an existing, empty job and checked it out to both workspaces. In Visual Studio, I typed in // Code added in Visual Studio and checked the change in. In Dynamics AX, my change was // Code added in AX. When committing changes, TFS detects a conflict and shows a dialog where you can see and merge changes:

MergeDialog

This is a very simple example, nevertheless you can see that is shows the changes I’m checking in, the changes done by others in newer versions and the resulting merged code (on the bottom). If there are several conflicts, I can resolve them one by one or simply accept one of versions of the files: ResolveAll

I can change the code in the bottom pane as needed. For example, I decided to merge comments by creating a completely new one:

ManuallyMerged

The code inserted to TFS is the result of this merge, of course:

ViewChanges

You may not like that the dialog uses the format of .xpo files and not a visual tree as in AX, but that’s how AX code and metadata are saved to source control systems and it’s usually better for merging anyway. By the way, .NET code is stored and compared exactly in the same way – as text files; it just has prettier formats. Don’t be too afraid of the .xpo format – it’s simple and you’ll need it in a few other cases, such as when reviewing changes line by line with annotate:

Annotate

It’s also worth mentioning that you may want to have a separate branch for each developer and merge code on branch-level rather than changeset-level. An advantage for merging is that it happens independently on AX, therefore you can use latest tools, regardless what version of TFS is integrated to AX. On the other hand, you usually want to integrate code together (and find any issues caused by that) as soon as possible, which is an argument against too many branches.

Target date not found in work calendar

“Target date not found in work calendar” was an error that was reported to me by a customer using workflow in AX2009. It was easy to find that it was thrown by WorkflowWorkCalendarDueDateProvider.isWorkingDay() but it was far from obvious why.

I tried to update the calendar in approval time limit, although it didn’t seem to be used in that moment (but it did automatically default to a calendar for a previous year):

ApprovalTimeLimit

This seemed to be the core problem – the calendar contained dates for a single year only and anything outside that year caused an error (because, as the error message correctly said, the target date was not found in work calendar).

Changing the calendar helped a little bit, but the workflow failed with the same error a moment later. I found that the calendar ID was saved in every single workflow step (WorkflowStepTable.Duration):

PackedWorkflowStep

 

It seems to me that:

  1. A calendar used in workflow should cover all possible dates (we can add additional dates to the calendar when needed, but we won’t have to ever change the reference to calendar).
  2. If it needs to be changed, the workflow configuration should be dropped and set up again.
  3. But it’s of course possible to update all workflow steps from code:
WorkflowStepTable   step;
WorkflowConfigDate  confDate;
str                 newCalendarId = 'Workflow';
;
ttsbegin;
 
while select forUpdate step
{
    confDate = WorkflowConfigDate::create(step.Duration);
    confDate.parmDailyCalendar(newCalendarId);
    confDate.parmHourCalendar(newCalendarId);
    step.Duration = confDate.pack();
    step.update();
}
 
ttscommit;

Data contract serialization from X++

You can decorate classes in AX2012 with DataContractAttribute in much the same way as in .NET with System.Runtime.Serialization.DataContractAttribute. The same is true also for DataMemberAttribute.

This is a simple data contract class in X++:

[DataContractAttribute]
class ItemContract
{
    str name;
 
    [DataMemberAttribute("Name")]
    public str parmName(str _name = name)
    {
        name = _name;
        return name;
    }
}

In .NET, you can serialize data contracts to XML (and deserialize from XML) with DataContractSerializer. What if you want to do the same with data contracts in X++?

I’m not aware of any such serializer in X++. You could write one, but it wouldn’t be a five-minute job. But there is a way how to actually use the .NET serializer.

As you surely know, AX2012 is able to compile X++ types to the Common Intermediate Language (CIL). Other “.NET” languages are compiled to CIL as well, which allows all of them to work together seamlessly. You can save a class written in a different language to a variable, call its methods, inherit from it and so on. (Don’t worry too much about differences between .NET, CLR, CIL, CLI etc. You just need to know that it’s all related to the same thing, usually called .NET).

When AX2012 generates CIL, .NET types for X++ classes and tables are created in Dynamics.Ax.Application assembly. For example, PriceDisc class is turned to Dynamics.Ax.Application.PriceDisc. If a piece of X++ code is executed in a CLR session, CIL types from Dynamics.Ax.Application are transparently used instead of original X++ types.

However, classes decorated by DataContractAttribute won’t be decorated by Dynamics.Ax.Application.DataContractAttribute in CIL. The X++ attribute is actually replaced by System.Runtime.Serialization.DataContractAttribute, therefore you’ll get a proper .NET data contract that can be serialized by DataContractSerializer. That’s likely what happens somewhere in AX kernel when you pass a data contract with parameters from AX to a report server, for instance.

By the way, I really like this approach. People from Microsoft managed to provide an easy way to define data contracts in X++ and still benefit from everything related to data contracts in .NET. Now it looks like an obvious solution, but who would have thought it about before?

Nevertheless what if we want to use the serializer from our own X++ code? First of all, let me introduce a C# method that will do all the serialization. All we have to do is to pass a data contract and we’ll get back the object serialized to XML.

using System;
using System.Runtime.Serialization;
using System.IO;
using System.Xml;
 
namespace Demo
{
    public class ContractSerializer
    {
        public static string Serialize(Object o)
        {
            Type type = o.GetType();
 
            if (!Attribute.IsDefined(type, typeof(DataContractAttribute)))
            {
                throw new ArgumentException(String.Format("{0} is not a data contract", type.FullName));
            }
 
            using (var stringWriter = new StringWriter())
            using (var xmlWriter = new XmlTextWriter(stringWriter) { Formatting = Formatting.Indented })
            {
                new DataContractSerializer(type).WriteObject(xmlWriter, o);
                return stringWriter.GetStringBuilder().ToString();
            }
        }
    }
}

You can put the class to a class library, add it to AOT and deploy it to both client and server.

Then we need to ensure ourselves that our X++ code will run in CIL. We’ll utilize SysOperation framework for that purpose:

class SerializationDemo extends SysOperationServiceController
{
    public static void main(Args args)
    {
        SerializationDemo demo = new SerializationDemo();
 
        demo.parmClassName(classStr(SerializationDemo));
        demo.parmMethodName(methodStr(SerializationDemo, serializeToInfolog));
        demo.parmExecutionMode(SysOperationExecutionMode::Synchronous);
 
        demo.run();
    }
 
    [SysEntryPointAttribute]
    public void serializeToInfolog()
    {
        if (!xSession::isCLRSession())
        {
            throw error("Must run CIL!");
        }
        // Here we'll do the job
    }
}

Do not omit the verification that we’re really in a .NET session. If the code ran in X++, it wouldn’t work, because we can’t simply pass an X++ object to a .NET method. If the code don’t execute in CIL, go to Tools > Options > Development and tick Execute business operations in CIL. Also, don’t forget to generate CIL.

If we’re in a .NET session, we can simply create an instance of a data contract class and send it to our method. Let’s create some data:

private DirPersonInfoData getPersonData()
{
    DirPersonInfoData person = new DirPersonInfoData();
 
    person.parmPersonName("John Doe");
    person.parmPersonPrimaryEmail("john(at)doe.com");
    person.parmPersonUserId("jdoe");
 
    return person;
}

Unfortunately, if you try to pass it directly to the serialization method:

DirPersonInfoData person = this.getPersonData();
Demo.ContractSerializer::Serialize(person);

it will fail with the error “Microsoft.Dynamics.AX.ManagedInterop.Object is not a data contract”.

The problem here is that the generated code uses a proxy class, which is not what we need, because proxy classes are not generated with attributes. Let’s tell AX that we’re working with a .NET object:

DirPersonInfoData person = this.getPersonData();
System.Object clrPerson = CLRInterop::getObjectForAnyType(person);
;
Demo.ContractSerializer::Serialize(clrPerson);

Now let’s put it together and finalize serializeToInfolog() method by adding exception handling and output to infolog:

[SysEntryPointAttribute]
public void serializeToInfolog()
{
    DirPersonInfoData person = this.getPersonData();
    System.Object clrPerson;
    System.Exception ex;
    str serialized;
 
    if (!xSession::isCLRSession())
    {
        throw error("Must run CIL!");
    }
 
    try
    {
        clrPerson = CLRInterop::getObjectForAnyType(person);
        serialized = Demo.ContractSerializer::Serialize(clrPerson);
    }
    catch (Exception::CLRError)
    {
        ex = CLRInterop::getLastException();
        throw error(ex.ToString());
    }
 
    info(serialized);
}

And this is how DirPersonInfoData will be serialized:

<DirPersonInfoData xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns="http://schemas.datacontract.org/2004/07/Dynamics.Ax.Application">
    <parmPersonCommunicatorSignIn />
    <parmPersonName>John Doe</parmPersonName>
    <parmPersonPrimaryEmail>john(at)doe.com</parmPersonPrimaryEmail>
    <parmPersonUserId>jdoe</parmPersonUserId>
    <parmWorkerTitle />
</DirPersonInfoData>

The fact that the same X++ code can be executed once in a native AX session and once in a CLR session is very powerful and you usually don’t have to care about how it works. Of course, it may be sometimes confusing. For example, you need to know how your code is executed in a given moment to use the right debugger.

Here I showed how to leverage it even more. The solution works because we can define a type in X++ and use it later as a CLR type. But it also depend on the fact how the CIL generator deals with DataContractAttribute, which is something you can only find by looking into CIL. A CIL decompiler comes handy if you want to play with these things.

Windows Azure Virtual Network with Active Directory

First of all, why would you want to create virtual machines in Windows Azure? There may be many reasons; let’s mention just a few of them:

  1. You don’t need any new hardware or adding more pressure on existing hardware.
  2. Creating virtual machines is very easy – you can do it in just a few clicks.
  3. You can easily build systems with several machines, for example if you need to test a clustered environment.
  4. You can easily change allocated resources, e.g. you may add additional processor cores and RAM for the time of compilation.

By the way, if you’re considering putting a production environment of Dynamics AX to Azure, you may want to wait for AX 2012 R3, where deployment to Windows Azure will be officially supported (as announced on Convergence 2013 EMEA).

In many cases, you’ll also need Active Directory (installation of Dynamics AX falls into this category). You could use your existing domain even in Windows Azure and that’s something what you probably want to do with production systems. But creating a new domain (or even several domains) is useful for many development and testing scenarios. And what’s equally important – you will get your own domain where you can do everything you want, which is typically (and fortunately) not the case with your real company domain. You may want to add new machines to a domain, create new users to test permissions, create users for AX lifecycle services, testing Group Policy and so and so on.

Before you start, you’ll need an Azure account. If you have an MSDN subscription, you’ll get monthly credit for Azure services – that’s the best way to start. Otherwise you can always use the free one-month trial.

Creating a network with Active Directory domain is relatively easy, it just requires several steps.

First of all, log into the Azure management portal and create a new virtual network. You can use Network Services > Virtual Network > Quick Create or Custom Create.NewVirtualNetwork800

Then create a new virtual machine and configure it as a domain controller. Use Install a new Active Directory forest in Windows Azure as a reference.

Create a new virtual machine from gallery (choose a template with Windows Server OS, of course).

NewVM800

Don’t forget to assign it to the newly created virtual network.

VMConfig2

Log into the new machine and configure it as a Domain Controller.

By the way, I used merely the Small VM size (1 core, 1.75 GB RAM).

When the domain controller is ready, go to the configuration of your virtual network and set the Domain Controller machine as a DNS server (use its Internal IP Address that you can find on VM’s Dashboard). You can use some additional DNS servers, if you want:

DNS400

The last step is creating a virtual machine (machines) for your real work (don’t forget to link it to the virtual network) and adding it to your new domain as usual.

If you’re in doubt which size to choose for Dynamics AX 2012, I’m using the Large one (4 cores, 7 GB RAM). You can always change it if you find it unsuitable for your purposes.

Creating sales orders via AIF in AX2012

I was asked for an example how to create a sales order through AIF document services (SalesSalesOrderService). Here is my minimalist variant in C# (for AX2012 and demo data):

var line = new AxdEntity_SalesLine()
{
    ItemId = "D0001",
    SalesQty = 42,
    SalesUnit = "ea"
};
 
var order = new AxdEntity_SalesTable()
{
    CustAccount = "US-003",
    PurchOrderFormNum = "xyz",
    ReceiptDateRequested = DateTime.Now.Date,
    SalesLine = new AxdEntity_SalesLine[] { line }
};
 
var orderList   = new AxdEntity_SalesTable[] { order };
var callContext = new CallContext() { Company = "USMF" };
var client      = new SalesOrderServiceClient();
 
try
{
    client.create(callContext, orderList);
    client.Close();
}
catch
{
    client.Abort();
    throw;
}

It doesn’t have to work for you out of the box (for example, you may have additional fields required by the contract), but it should give you an idea how it looks like, without adding unnecessary complexity.

If you’re not familiar with AIF document services, you may want to look at AX 2012 Documentation Resources for AIF and Services.