Generic types in X++ (AX 7)

This blog post assumes that you’re familiar with generic types. If it’s not the case, I suggest you look at my previous blog post before continuing.

You can’t create generic types and methods in X++, so why would you care about generics at all? The main reason is that it’s used heavily in .NET codebase and there are many .NET APIs that you may want to use from X++. Without being able to use generic types and methods, a lot of .NET functionality would be inaccessible to you.

When you want to use class or methods that use generics, you have two main options.

Firstly, you can somehow handle it in X++. For example, let’s say you want to get file names with the help of Dictionary::EnumerateFiles(), which returns a generic collection of strings (IEnumerable<string>). You can avoid the problem of declaring generic types by utilizing the var keyword, like here:

var files = System.IO.Directory::EnumerateFiles('c:\\');
var enumerator = files.GetEnumerator();
 
while (enumerator.MoveNext())
{
    info(enumerator.Current);
}

The var keywork is just a shorthand; the real types (such as IEnumerable<string>) are still used, as you can verify in the debugger:

As you can see, you often can work with generic types without explicitly using any syntax specific to generics. Using var is just one of such techniques.

But not everything can be done in X++, or it’s not very easy to do it there. Another option is dealing with generics in a separate library (usually in C#) and exposing a non-generic interface for consumption in X++. It’s not just about generics, but anything that can’t be reasonably done from X++. For example, if you want to find names in a list that are longer than a defined limit, you can use the FindAll() method, which expects a parameter of type Predicate<T>. Rather than trying to deal with it in X++, you can write a facade method in a C# library and later call this simpler method from X++.

public List<string> namesLongerThan(List<string> list, int numOfChars)
{
    return list.FindAll(e => e.Length > numOfChars);
}

It works great and working with such libraries is easier with every version of AX (you can use automatically deployed VS projects in AOT in AX 2012 and project references in AX 7), but the non-generic interface available to X++ will obviously lose all the power of generics.

Support in X++

I was thrilled when I learnt that X++ in AX 7 supports generic .NET classes when declaring variables and creating objects – I didn’t hear about this possibility before. I have many cases when using generics would be very useful and I was keen to explore what I can do with this feature.

Unfortunately, I’m not too impressed so far – I’ll explain why in a moment. There is probably a reason why this feature isn’t loudly promoted; it looks like a work still in progress.

Let’s start with something that actually works.

If you want to create instances of generic classes, you can use the same syntax as in C# (with angle brackets, i.e. <>).

System.Collections.Generic.List<int> users = new System.Collections.Generic.List<int>();

To simplify the code and make it more readable, we can avoid repeating all the namespaces by utilizing the using keyword.

using System.Collections;
…
Generic.List<int> users = new Generic.List<int>();

You can use any type you like for the type parameter, including those defined in AX. For example, here I’m create a queue of buffers of the UserInfo table:

Generic.Queue<UserInfo> userQueue = new Generic.Queue<UserInfo>();

Generic types are not limited to collection classes, of course. One thing I tried and works better than I expected are nullable types. It’s sometimes important to distinguish whether there is no value at all or if there is a value but it happens to have a default value of the given type. For example, whether a calculation shows that a balance is 0 is or whether the balance hasn’t yet been calculated are clearly two very different cases. AX itself isn’t good in dealing with these things, but .NET comes with a solution – nullable types. I can create an instance of Nullable<int> and assign either a null reference or an actual number, and later check if it has a value at all and what it is.

System.Nullable<int> c = null;
info(c.HasValue ? strFmt("Value %1", c.Value) : "No value");
c = 0;
info(c.HasValue ? strFmt("Value %1", c.Value) : "No value");

Bugs and unsupported features

Unfortunately, I’ve run into quite a few problems. (For reference, note that I’m using a demo VM with Platform Update 5.)

Problems with IntelliSense

When I declare a variable of a generic type (e.g. System.Nullable<int> c = 5;) and try to use the variable, IntelliSense doesn’t offer any members (after typing c.). This seems to be a more general problem with IntelliSense for .NET types. But it gets worse – if I try the same after building the project, IntelliSense throws an error:

Name System.Nullable<int> is invalid. Metadata name must be non-null and can not contain whitespace or invalid path characters.

Missing type checks

X++ compiler doesn’t always check types of arguments when working with generic types, which defeats the whole purpose of generic types. This may have the same cause as missing IntelliSense information. For example, the following code compiles without any error, although I’m trying to assign string values to a collection of integers. The runtime will try to convert these strings to integers and throws an error only if it fails. Using wrong types should fail already on compilation.

System.Collections.Generic.List<int> list = new System.Collections.Generic.List<int>();
list.Add(1);
list.Add("2");     // Converts to int
list.Add("hello"); // FormatException at runtime

‘Real’ type can’t be used

As far as I know, you can use any X++ type as the type parameter, with a single exception: real. If you try to write something like this,

new System.Collections.Generic.List<real>();

the compiler will complain that a ‘(‘ is expected (which suggests that it fails already during parsing).

Failure with var

Another problem is with the var keyword. You may want to try to simplify your code by declaring the type of variable as var, like here:

var list = new System.Collections.Generic.List<int>();

Unfortunately, X++ compiler fails horribly on this code – it throws an exception from ILGeneratorSweeper.VisitClrType(), complaining about an unsupported type.

Generic methods can’t be called

A serious flaw is that generic methods can’t be called from X++. It’s great that I can create a generic collection in X++, but what will I do with it if I can’t pass it to any method accepting generic collections?

For example, I may want to extract distinct elements from a collection, which can be easily done in .NET with an extension method Enumerable.Distinct<TSource>(). If I try to call it from X++ (System.Linq.Enumerable::Distinct(list)), X++ compiler seems to ignore the generic parameter and it looks for non-generic Distinct(), which doesn’t exists. It’s also visible in IntelliSense, which finds the method, but it claims it’s Distinct() instead of Distinct<TSource>(). Although IntelliSense will offer you the method, the compiler will later say that it doesn’t exist.

Method 'Distinct(System.Collections.Generic.IEnumerable`1)' is not found on type 'System.Linq.Enumerable'.

Casting

Another serious problem is that you can’t cast generic types to generic interface they implement. For example, you have a generic list of integers(List<int>) and you want to assign it to a variable of IEnumerable<T>.

List<T> implements IEnumerable<T>, so it should be all right, but it fails, saying: Cannot implicitly convert from type ‘System.Collections.Generic.List’ to type ‘System.Collections.Generic.IEnumerable’.

There is a workaround for this, but it means giving up compile-time type control:

CLRObject l1 = new System.Collections.Generic.List<int>();
System.Collections.Generic.IEnumerable<int> l2 = l1;

By the way, assignments to the non-generic IEnumerable interface work fine:

System.Collections.IEnumerable list = new System.Collections.Generic.List<int>();

Extending generic classes

One more thing I tried was inheriting an X++ class from List<str> (inheriting from generic types in this way isn’t uncommon in .NET), but it doesn’t compile in X++. Nevertheless this definitely isn’t the most important feature.

Conclusion

Although the ability to declare and instantiate objects of generic types directly in X++ is nice, it doesn’t work very well in the moment. Also, many related features (such as the support for generic methods) are still missing, therefore if you want to use generics in X++, you still have to understand how to work around all the limitations.

After reading the full specification of C#, I realize how generics (and covariance/contravariance) greatly increases the complexity of language design, but I hope we’ll see more generics in X++ in future. It’s necessary for easy interoperability with .NET code and obviously such a great feature would be very useful in pure X++ as well.

Introduction to generics

My next blog post will cover a few topics regarding generics in AX 7 and because many X++ developers aren’t familiar with this concept (as it’s not used in X++ directly), I thought it would be to explain what it is.

Imagine that you’re designing a collection class (such as a list) in a statically-typed object-oriented language and you want this collection to be usable for any type.

To define a single class supporting all types of objects, you can let it store instances of the Object class. Because everything is an object, you can successfully put anything there. It may look like this:

public class List
{
    public void Add(Object value) {}
    public Object GetFirst() {}}

I realize that it’s a bit different in X++, because primitive types don’t extend from Object and it offers anytype, but let’s stick to the traditional object-oriented design.

Such a collection class will work, but it’s not very safe, because it’s possible to inadvertently mix values of different types. Also, you have to cast values from Object back to the expected type, which will fail at runtime if you use a wrong type (although it compiles correctly).

For example, the following code tries to create a list of numbers, but one of the values is a string.

List numbers = new List();
numbers.Add(1);
numbers.Add(2);
numbers.Add("3");

Because the collection accept all objects, this code is valid and you have no way how to enforce it to accept only numbers at compile time.

If somebody iterates the list and try to assign each value to an int variable, it will throw a runtime exception at the third element.

We could create a separate class for a list of integers, another for a list of strings, yet another for a list of customers and so on, which would allow us to detect such error during compilation, but it would be completely impractical. Here is when generics come to rescue.

With generics, we can define the common logic in a generic class and leave out the specific type of values. For example, we’ll always have an Add method accepting a value of a certain type, but we don’t have to specify which type it will be. The type will be selected by developers using our generic class.

Using the C# syntax, the definition of our generic list will look like this:

public class List<T>
{
    public void Add(T value) {}
    public T GetFirst() {}}

T is a placeholder for the specific type. It’s used in the class name (making clear that this is a generic class with a single type parameter) and at all places where we want to use the type (such as the parameter of Add()).

When we want to use this generic class, we always have to specify which type it will store. For example, List<int> is a collection of integers, List<Customer> a collection of customers and so on. The compiler replaces all occurrences of T with the given type, therefore List<int> will behave like if it was defined in this way:

public class List
{
    public void Add(int value) {}
    public int GetFirst() {}}

This is a very different from using a list accepting all objects – trying to add anything else than int will cause a compilation error, the return type of GetFirst() is int and therefore there is no need for casting from Object and so on. It allows us to define our list and all the common logic just once and still have a potentially infinite number of strongly-typed list classes for any existing or future type.

Generic methods

The same concept of generics can also be applied to individual methods.

The snippet below shows how you could declare a method for reversing lists, which accepts one list and returns another list of the same type.

List<T> reverseList<T>(List<T> listToReverse)
{}

Constraints

Sometimes you don’t want to support all types, but you have certain limitations – it must be a class, it must implement a certain interface or something like that. C# allows adding these constrains by a kind of where class, as documented in Constraints on Type Parameters (C# Programming Guide).

The next example shows a method for creating instances of various classes by calling their public parameterless constructor. Because not every type has such a constructor, I had to add a constraint for such types (where T : new()) – trying to call new T() wouldn’t compile if I omitted the constraint.

public T CreateObject<T>() where T : new()
{
    return new T();
}

Covariance and contravariance

Without going into details, it’s worth mentioning that the type parameter you use doesn’t always have to match the declared type exactly. In some situations, you can use compatible type. For example, if a method is declared to accept IEnumerable<FileSystemInfo>, you can send there an instance of List<DirectoryInfo> (FileSystemInfo is a parent of DirectoryInfo). If you with to learn more, look at Covariance and Contravariance on MSDN.

Conclusion

With generics, you can write code capable of working with many different types and still keep strict type control at compile time. It’s commonly used in many modern programming languages, such as C#, Visual Basics and Java.

It’s official – AX 7 will support deployment on-premises

Make sure you familiarize yourself with new (upcoming) deployment options for Dynamics 365 for Operations. In short, you’ll be able to run it outside cloud (e.g. on your own hardware) and then either synchronize data to cloud or keep them locally. Of course, not having data in cloud means missing some cloud features such as Power BI. There will be also a change in licensing, because the usual subscription licenses include Azure resources, which you won’t need if you keep everything locally.

This is a huge news for quite a few customers who couldn’t go to cloud because of the distance from nearest Azure data centers, local law or company policies.

JSON-based custom service with parameters (AX 7)

Dynamics 365 for Operations deploys custom web services in two ways: as SOAP-based services and JSON-based services. AX developers are often familiar with SOAP services (which were used in AX 2012), but JSON-based ones are new to them.

One particular challenge is passing arguments to a service. Let’s say we have an Add operation, which can sum two numbers.

public int Add(int a, int b)
{
    return a + b;
}

To call the service, we have to provide values for both arguments, a and b. Because the expected format is JSON (Java Script Object Notation), we have to provide a JSON string describing an object with two properties (a and b) and their values. This is it:

{ a: 2, b: 5 }

Note that names of the properties are important – they must match parameter names in the X++ method.

Because building JSON strings by yourself can be cumbersome (especially with more complex parameters), a better approach is working with objects and leaving conversion to JSON to a serializer.

For example, you can build a simple class,

public class AddContract
{
    public int a { get; set; }
    public int b { get; set; }
}

create an instance with required values and call JsonConvert (from Newtonsoft.Json) to convert it to string:

AddContract contract = new AddContract { a = 2, b = 5 };
string json = JsonConvert.SerializeObject(contract)

If you need AddContract class just at this single place, maybe it’s not worth creating it at all. We can use an anonymous object instead and it will still work the same.

var contract = new { a = 2, b = 5 }; // Anonymous object
string json = JsonConvert.SerializeObject(contract)

When we have the JSON string, we have to send it to the service. The implementation below assumes that you use AuthenticationUtility from the sample solution from Microsoft.

// Prepare HTTP client, including authentication
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(ClientConfiguration.Default.UriString);
client.DefaultRequestHeaders.Add(OAuthHelper.OAuthHeader, OAuthHelper.GetAuthenticationHeader());
 
// Define parameters
var contract = new { a = 2, b = 5 };
 
// Create a request
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,
                                                    "api/services/MyServiceGroup/MyService/Add");
request.Content = new StringContent(JsonConvert.SerializeObject(contract),
                                    Encoding.UTF8,
                                    "application/json");
 
// Run the service
var result = client.SendAsync(request).Result;
 
// Display result to console
if (result.IsSuccessStatusCode)
{
    Console.WriteLine(result.Content.ReadAsStringAsync().Result);
}
else
{
    Console.WriteLine(result.StatusCode);
}

If you’re using WebRequest instead of HttpClient (as in Microsoft sample code), you can use add parameters to the request in this way:

var contract = new { a = 2, b = 5 };
string json = JsonConvert.SerializeObject(contract);
 
Byte[] byteArray = Encoding.UTF8.GetBytes(json);
 
request.ContentLength = byteArray.Length;
request.ContentType = "application/json";
 
using (Stream dataStream = request.GetRequestStream())
{
    dataStream.Write(byteArray, 0, byteArray.Length);
}

It’s clearly nothing difficult, just make sure that your properties in JSON exactly match parameter names. For example, if parameters of my Add operation were prefixed with underscore (as usual in X++), the JSON string would have to be { _a: 2, _b: 5 }.

AX7 development tools without VM

If you want to run and/or modify AX 7 (Dynamics 365 for Operations), you get a whole virtual environment configured by Microsoft and you either deploy it to Azure or run as a local virtual machine on Hyper-V.

I have a huge laptop with 32 GB RAM and two SSD disks built specifically for running AX 2012 and AX 7 VMs and I also have a few environments in Azure (my MSDN subscription gives me some credit for Azure every month), therefore I have all I need for development. But sometimes I don’t have access to these environments, such as when I travel just with a small laptop, and I would still like to review some X++, verify something when answering questions in forums and so on, and having AX development tools with me would be a great help.

I tried it and found that setting it up wasn’t very difficult, as I’m going to show below. Just let me make it clear – I’m talking about having access to Application Explorer, projects, designers and code, not about building and running Dynamics AX. It would be possible, but it would require more effort and my small laptop isn’t even powerful enough to do that. When I want to develop something, I use one of my proper development environment.

First of all, you need a machine with Windows and Visual Studio 2015. To show the process from the beginning on an empty box, I’ll use a virtual machine in Azure based on the image called Visual Studio Enterprise 2015 Update 3 with Universal Windows Tools and Azure SDK 2.9 on Windows 10 Enterprise N (x64).

Files and website

We won’t build the environment from scratch – we’ll copy the application from an existing development environment. Go to an environment you want to copy, find the folder containing packages and WebRoot (most likely J:\AosService), make a copy and put it to your machine. In my example, I’ve put it to c:\AX7.

Find web.config file inside WebRoot folder and update paths according to the new location. Edit > Find and Replace > Quick Replace (or just Ctrl+H) in Visual Studio will help you with it.

Then create a site referring to WebRoot. Because IIS isn’t enabled by default, go to Windows features and add it.

Open IIS manager and add a new website.

Set the name to AOSService, Physical path to the WebRoot folder (C:\AX7\AosService\WebRoot in my case) and Port to something that isn’t already used.

We’re not going to run the website; it’s here just for Visual Studio to locate configuration. If we wanted to actually run it, we would have to pay much more attention to the configuration of both the website and the application pool.

Visual Studio tools

When you have files and the service ready, it’s time to install the Visual Studio extension containing all tools for AX (Application Explorer, project types, the menu and so on). Download a binary update from LCS and extract it to a folder (D:\AllBinary71Update3Updates in my case). Then go to DevToolsService\Scripts, find Microsoft.Dynamics.Framework.Tools.Installer.vsix, double-click it to start the installation and walk through the installation wizard.

If you want to be able to create AX projects (which may be useful), you also have to copy MSBuild target files (which are references by project files) to the expected location. You can do it, for example, by executing the following Powershell script (with the Script folder as the current location).

$dir = New-Item "C:\Program Files (x86)\MSBuild\Microsoft\Dynamics\AX" -Type Directory
cp *.targets $dir

When you run Visual Studio (as administrator), you should be able to open Application Explorer, view code, create AX projects and so on.

But one important thing is missing. When you’re trying to locate an object or you’re familiarizing yourself with the application, you’ll likely use cross references, which we don’t have there in the moment. Let’s fix it.

Cross references

Cross references are stored in a SQL Server database called DYNAMICSXREFDB, therefore go to the source environment, create a full backup of this database and copy the file to your machine. If you have SQL Server installed, connect to it, if not, not an issue – Visual Studio comes with a local DB. You may want to download and install SQL Server Management Studio and then simply connect to a server called (localdb)\MSSQLLocalDB in the same way as if it was a regular SQL Server.

Restore DYNAMICSXREFDB from the file.

Give yourself sufficient permissions to the database, such as making yourself a database owner.

You have to tell Visual Studio where it’ll find the database, therefore open {Documents}\Visual Studio 2015\Settings\DefaultConfig.xml and change the value of CrossReferencesDatabaseName to (localdb)\MSSQLLocalDB (or your database server if you don’t use local DB).

Update (5 April 2017): Because Visual Studio doesn’t respect CrossReferencesDatabaseName property when updating cross-references and it uses the value of DatabaseServer instead, update DatabaseServer element to the same value as CrossReferencesDatabaseName. Otherwise any attempt to build X++ code may fail, with Visual Studio complaining about not being able to connect to the cross-reference database. This step won’t be needed after this bug gets fixed.

Start Visual Studio again and you can start using references as usual.

Although it doesn’t build a full environment with all features, it fulfills its purpose. Now I can easily carry all AX 7 code with me, exploring it on plane, quickly checking code or property names when somebody asks in a forum and so on. My intention isn’t to stop using VMs from Microsoft for development and testing – on machines powerful enough to run AX (don’t forget that the compiler is designed for 16 GB RAM, for example), I can use the VM as well, so configuring the whole environment on my own installation of Windows would be worth the effort. I did this when I couldn’t use the VM and the time spent with setting it up it is already paying off. Documenting it for others took much more time than that. 🙂