Discovery of JSON-based custom services in AX 7

If you download AX integration samples from GitHub, you’ll see (in JsonConsoleApplication project) that you can call JSON-based custom services by code like this:

var request = HttpWebRequest.Create(ClientConfiguration.Default.UriString + "api/services/UserSessionService/AifUserSessionService/GetUserSessionInfo");
request.Headers[OAuthHelper.OAuthHeader] = OAuthHelper.GetAuthenticationHeader();
request.Method = "POST";
request.GetResponse();

It will call the operation and return its return value as JSON:

{
    "$id":"1",
    "AOSLocaleName":"en-US",
    "AXLanguage":"EN-US",
    "Company":"DAT",
    "CompanyTimeZone":58,
    "CurrencyInfo": {
        "$id":"2",
        "CurrencyCode":"USD",
        "Description":"US Dollar",
        "ExchangeRate":100.0,
        "ISOCurrencyCode":"USD",
        "Prefix":"","Suffix":""
    },
    "IsSysAdmin":true,
    "UserId":"wintermute",
    "UserPreferredCalendar":0,
    "UserPreferredTimeZone":18
}

This is what happens when you use the POST method of HTTP; if you switch to GET, you’ll actually get some information about the service.

Therefore if you merely change the value of request.Method:

var request = HttpWebRequest.Create(ClientConfiguration.Default.UriString + "api/services/UserSessionService/AifUserSessionService/GetUserSessionInfo");
request.Headers[OAuthHelper.OAuthHeader] = OAuthHelper.GetAuthenticationHeader();
request.Method = "GET";
request.GetResponse();

you’ll get a very different response:

{
    "Parameters":[],
    "Return": {
        "Name":"return",
        "Type":"AifUserSessionInfo"
    }
}

You can see that the operation doesn’t expect any parameters and it returns a single object of AifUserSessionInfo type. This gives you some limited information about how to use this service.

You can also use GET requests to discover existing services and their operations.

/api/services gives you a list of all service groups:

{"ServiceGroups":[{"Name":"AxClient"},,{"Name":"UserSessionService"}]}

/api/services/UserSessionService provides a list of services in the service group:

{"Services":[{"Name":"AifUserSessionService"}]}

/api/services/UserSessionService/AifUserSessionService shows all operations of the individual service:

{
    "Operations": [
        {"Name":"ApplyTimeZone"},
        {"Name":"GetAccessRights"},
        {"Name":"GetPartitionKey"},
        {"Name":"GetPartitionKeysForUser"},
        {"Name":"GetUserSessionInfo"},
        {"Name":"IsSinglePartitionSystem"},
        {"Name":"RemoveTimeZone"}
    ]
}

Don’t forget than opening an URL in browser makes a GET request, therefore you don’t have to write any code to get this kind of information about custom services.

But maybe you would like something a bit more sophisticated, which is the topic of the next post: Open API for JSON-based custom services in AX 7.

Integration with AX 7 in cloud

I’m getting a lot of questions along the lines of “how on earth can we integrate our on-premises systems with AX 7 running in cloud?”, therefore let me share some of my thoughts.

The first important point to realize is that “to integrate” means many different things, so there is no single solution for it. On the contrary – you know have more options than before, because you can easily utilize many existing cloud services, which weren’t that useful when your ERP wasn’t running in cloud. Although I mention quite a few options below, the list isn’t comprehensive by any means.

You have to take many things into account when designing your solution: which system triggers the integration (AX, external systems), what’s the trigger (schedule, user activity…), whether you need a response in synchronous manner or you can utilize queues (which is better for load balancing), what are requirements for performance, reliability, monitoring and so on and so on. I’m not going to discuss these things in detail, but you mustn’t forget about them.

Below I look at some integration options based on which system is triggering integration. There are quite a few links, if you want to learn more.

From your servers / workstations

The most straightforward approach is sending requests from your own machines to AX.

It may be a from a desktop application (the Excel add-in calling OData services exposed by AX 7 is a good example) or a server process (e.g. a service monitoring a network folder and pushing files to AX).

If you want to call AX directly, you can use its services (especially OData and custom services), including pushing files through web services (e.g. data management API), but it’s often not the best strategy. For example, you may want to send messages even if AX is down for maintenance and therefore you merely put them to some storage (Service Bus queue, blob storage, file storage…) where AX will find them when it’s back online.

Or maybe you have some additional logic to run, such as a workflow with conditions, message transformations, aggregation with other data and so on. You can utilize things like Logic Apps or Azure Functions for this purpose. Alternatively, you can run your logic on-premises before sending messages to cloud; then you can use BizTalk, for instance.

Logic Apps can also read data from storage and pushing it to AX, so you don’t have to implement it by yourself.

From AX

Running a piece of code in AX (usually in a batch) is a natural choice for many AX developers, but some aspects are a bit different in cloud.

In past, people often put files to import (to take this direction as an example) to a network folder, where AX read them on schedule. Now they ask how to put files to production VMs, which simply isn’t possible. You also can’t expect Azure applications to read files directly from your local hard disk. If you want to use this approach, you must store your files elsewhere, such as in an Azure storage in your subscription, on FTP and so on. For example, you can put files to OneDrive or a file storage mapped as a drive and let AX to read them from there via an API (although you may want to leave it to a Logic App, as demonstrated in File based integration using Logic Apps).

You can also call web services from X++, which includes even web services offered by your on-premises systems (exposed through Azure AD Application Proxy or Azure Relay, for example).

You can also call middle-tier services and execute any other code you like.

From a middle-tier service

There is a third option, when integration is orchestrated by another service in the middle. For example, you may have a logic app that runs on schedule, reads data from your on-premises Oracle database and uses the connector for AX 7 to create records in AX. Then you don’t have to develop any on-premises application pushing data to cloud, you merely install a gateway).

Some people are afraid that there is no way how to integrate on-premises and cloud systems, but that’s obviously not the case. You have many options, you just must choose the right one for your particular goal. It’s different than if everything runs in your own server room and it’s likely confusing if you’re not familiar with Azure (with new types of storage, app authorization and so on), but that’s what always happen when you start working with different architecture. You’ll also often combine several services, rather than having a single universal solution for integration.

Save storage with data deduplication

This blog post isn’t directly about software development, because I’m going to look at a certain feature of Windows Server, but my primary motivation is still dealing with development and testing.

When you want to set up AX 7 (Microsoft Dynamics 365 for Finance and Operations, Enterprise Edition :)) environments for development, testing or demonstration purposes, you can either deploy them to Azure or download a virtual disk and create a virtual machine locally or in a custom data center. Many partners (especially VARs) prefer hosting VMs by themselves, because they’ve already invested to data centers and they’re used to hosting virtual machines with previous versions of AX.

They may need a large number of VMs for AX 7 (for many projects and many developers), but it’s not a problem for computation resources, because just a fraction of these VMs are actually running. For example, a developer may have VMs for five projects but only one of them is used for development at any time. The problem people complain about is the storage requirement, because the virtual disks are pretty big (a single fresh VHD takes approximately 70 GB and it grows when used).

Most data in these virtual disks is the same, therefore if there was a feature to somehow store the same bits just once, it would drastically reduce the amount of data in physical storage. Fortunately it’s exactly what Windows offers.

Windows Server 2012 introduced the Data Deduplication feature, which detects identical blocks of data (not whole files, which would be useless for virtual disks) and stores them only once, keeping a sort of a link from the other places. It’s completely transparent to you – you work with all files in the same way as before. Windows Server 2012 R2 then added extra support for virtual disks in VDI (Virtual Desktop Infrastructure) scenarios, therefore this is the minimum version you want to use. (Windows Server 2016 supports Data Deduplication too, of course, and have added a few improvements.)

According to Microsoft measurements, the usual space saving in these scenarios is 80-95% and I would indeed expect high numbers with AX VMs, because they’re almost the same. This means that any additional VM will need (after deduplication) just a few extra gigabytes of storage and having many VMs won’t be a problem anymore.

I haven’t try it by myself, though, because I don’t need it at home and clients have specialized infrastructure guys dealing with these things. If you have practical experience, you can share it in comments below this post.

If you want to learn more about data deduplication in Windows Server, the latest documentation is here.

Catching exceptions in AX 7

In this blog post, I briefly recapitulate how to throw and catch exceptions in X++ and introduce a new way of handling CLR exceptions in AX 7.

X++ exceptions

When you want to throw an exception in X++, you typically do it by something like this:

throw error("It's broken!");

It’s a functional equivalent of adding a message to infolog and throwing Exception::Error.

infolog.add(Exception::Error, "It's broken");
throw Exception::Error; // number 3

As you see, an exception in X++ is (or used to be, as I’ll discuss later) just a number, because enums are backed by integer values. It doesn’t come with any extra information, not even the message. Adding messages to infolog is a separate process; you can add error messages to infolog without throwing any exceptions and throwing exceptions without adding anything to infolog.

When you want to catch an exception, you’ll use a catch clause with the right value of Exception enum (usually Exception::Error) or you’ll handle all exceptions (by a catch clause without any type).

The following statement will work for all errors, but you don’t get any information about what error it was:

catch (Exception::Error) {}

Just knowing that there is an error doesn’t allow you to react differently to different errors, therefore you’ll rarely see code in AX trying to recover from a particular error.

CLR exceptions (in general)

It’s very different in Common Language Runtime (CLR). Exceptions there are objects (instances of classes extending System.Exception) and they contain a lot of information that can help you to identify what happened. For example, you can look at the type of exception (e.g. ArgumentNullException or FileNotFoundException) and react accordingly. The error message is a part of the object too, not just lying somewhere in infolog. The exception also comes with the stack trace, so you can easily see where it was thrown from.

Because the classes form a hierarchy, you can also handle exceptions in a hierarchic way. This is what you can write in C#:

catch (FileNotFoundException ex) {}
catch (Exception ex) {}

The system will try catch clauses one by one, going from the top down, and will use the first compatible clause it finds. If a FileNotFoundException is thrown, the first block will be used, but all other exceptions will go to the latter one, because they’re not FileNotFoundException but they’re all compatible with System.Exception, which its their common base class.

Exception::CLRError

Sometimes you have to handle CLR exception in X++, because you can use .NET libraries from X++ and such libraries can throw their usual (CLR) exceptions. The traditional approach is catching Exception::CLRError, getting the exception object from CLRInterop::getLastException() and extracting information from it. It’s further complicated by the fact that the actual error is usually wrapped in TargetInvocationException.

This is how you can handle FileNotFoundException in X++:

try
{
    System.IO.File::ReadAllText('c:\\nothing.here');
}
catch (Exception::CLRError)
{
    System.Exception wrapperEx = CLRInterop::getLastException() as System.Reflection.TargetInvocationException;
    if (wrapperEx)
    {
        if (wrapperEx.InnerException is System.IO.FileNotFoundException)
        {
            warning(wrapperEx.InnerException.Message);
        }      
    }
}

It works, but it’s cumbersome and hard to understand.

Catch with object reference

Fortunately AX 7 offers a new, much better option. Let me start by refactoring the previous example:

System.IO.FileNotFoundException fileNotFoundEx;
 
try
{
    System.IO.File::ReadAllText('c:\\nothing.here');
}
catch (fileNotFoundEx)
{            
    warning(fileNotFoundEx.Message);
}

This is obviously much shorter and easier to follow. In catch, I don’t have to use only values of the Exception enum anymore, I can also use exception objects. The system checks the type, selects the right catch clause and passes the exception object there. It’s almost the same as in C#, except of the fact that you can’t define the exception type directly in the catch condition; you have to declare a variable in an outer scope.

As in C#, you may have several catch clauses for different types of exceptions and benefit from the hierarchical nature of exception types. For example, the following snippet handles two types of exceptions in a special way and all remaining exceptions go to the last catch clause.

System.Exception                generalEx;
System.FieldAccessException     accessEx;
System.IO.FileNotFoundException fileNotFoundEx;
 
try
{}
catch (accessEx)
{
    warning("Field access");
}
catch (fileNotFoundEx)
{
    warning("File not found");
}
catch (generalEx)
{	
    warning(ex.Message);
}

ErrorException

All right, so X++ errors can be caught with catch (Exception::Error) and CLR exceptions by their particular type, but isn’t all code now executed by CLR? Does it mean that X++ exceptions use a different mechanism than CLR exceptions, or they’re just normal CLR exceptions under the hood?

You can find the answer in debugger, which reveals that “native” AX exceptions are indeed implemented as CLR exceptions:

The object (as you can see in debugger) has all usual properties, such as Message, Source, StackTrace and so on. Note that you can see the same behavior in AX 2012 if you debug CIL generated from X++.

Now what if you want to access the properties when handling an exception, e.g. to include the stack trace in a log? If you catch Exception::Error, you won’t get any details, but what if you try to catch ErrorException in the same way as any other CLR exception? If you think it should be possible, you’re right – the following piece of code successfully catches an X++ exception and shows how you can access its properties.

Microsoft.Dynamics.Ax.Xpp.ErrorException xppEx;
 
try
{
    throw error("It's broken!");
}
catch (xppEx)
{
    this.log(xppEx.StackTrace);
}

The problem that all X++ errors have the same exception type (ErrorException) is still there, therefore handling different errors in different ways is still hard, but you can now easily find which message belongs to the exception (without digging into infolog), where it was thrown from and so on.

By the way, I also wondered what would happen if I tried to catch both Exception::Error and ErrorException, because they’re internally the same thing.

Microsoft.Dynamics.Ax.Xpp.ErrorException xppEx;
 
try  
{
    throw Exception::Error;
} 
catch (Exception::Error)
{
    info("Exception::Error");
}
catch (xppEx)
{
    info("ErrorException");
}

Such code compiles without any problem and the resulting CIL will actually contains two catch clauses for the same exception type (ErrorException). It means that the top one always wins, regardless whether it’s catch (Exception::Error) or catch (xppEx). Using both for the same try statement makes little sense, but at least we now know that nothing catastrophic happens if somebody does it by mistake.

Conclusion

The native implementation of exceptions in X++ is very limited in comparison to CLR exceptions. The inability to distinguish between different kinds of errors makes meaningful recovery from errors virtually impossible and the lack of associated information (such as the message) makes even mere logging quite cumbersome. This doesn’t apply to CLR exceptions and their handling in X++ is even easier than before, thanks to the new ability of catch clauses. The fact that we can use this approach for X++ exception as well means that we can easily work around one of the limitations and access properties such as message and stack trace even for them.

It seems that we’re just a small step from being able to work with exceptions in X++ in the same way as in .NET. If we could throw exception objects (e.g. throw new MissingRecordException()), we would be able catch this particular type of exception and implement more robust logic for recovery from errors.

It would also help if X++ was extended to support declaration of the exception type directly in catch (e.g. catch (MissingRecordException ex)).