by Peter Villadsen
December 2013

 

The Microsoft Dynamics AX 2012 system allows an individual table field, in any one record, to contain multiple values. Typically this is done by creating an extended data type (EDT) that is an array type, and then using the EDT as the type for a field in a table. The most well-known case is the Dimension field of the EDT named Dimension. The Dimension type is shown in the AOT image, along with its array elements.

 

 

This is a flexible and dynamic feature. As soon as the user adds a new element to the Dimension type’s list of array elements, everything immediately lights up with the new dimension everywhere the type is used.

 

1.  X++ Support

The X++ language supports these array fields without problems. Let us imagine that the table called PVsTable has a field called DimensionField of type Dimension. The following are legal X++ code snippets: 

// X++, code example 1.
PVsTable t;  // PVsTable has one field, named DimensionField, of type Dimension.

Select DimensionField    from t;  // Select all array elements in DimensionField.
Select DimensionField[2] from t;  // Select the array element indicated by the index 2.

   The first select statement selects all the array elements of the DimensionField, while the second fetches only the array element for index 2. In all code, values of type Dimension are indexable by an integer value.

 

2.  Business Connector: Challenge and Trick

There is no API that enables you to get the value of an individual array element from the DimensionField field through the .NET Business Connector of AX. Instead, you use a little trick to get individual elements. Let’s set the stage with a little sample C# code:

Axapta ax = new Axapta();  // C#, code example 2.
 
ax.Logon("", "", "", "");
 
using (AxaptaRecord rec = ax.CreateAxaptaRecord("PVsTable"))
{
    // Execute the query on the table.
    rec.ExecuteStmt("select firstonly * from %1"); // All indexes are fetched.

    // This call does not return an array! Returns first element only.
    var f1 = rec.get_Field("DimensionField") as string;

    // This call returns the correct value for index 2
    var f2 = rec.get_Field("DimensionField[2]") as string;
}

ax.Logoff();

As you can see above, the get_Field method on the record does not actually get the array of values, but merely the first value in the array; which is at index 1. It can certainly be questioned whether this is a reasonable choice or not, but that is the behavior in place since the very first versions of the business connector.

 

3.  Access any Individual Element

If you want to access individual elements, you have to append the index you want by using a syntax borrowed from array indexing:

var f2 = rec.get_Field("DimensionField[2]") as string;    // C#, code snippet 3.

 

4.  Proxies

A proxy is a C# class which represents an AX object such as a class or table.  The AX plug-in for Visual Studio generates the proxy code when you drag a class or table from the Application Explorer onto your project in the Solution Explorer. Your project must first be added in MorphX under AOT > Visual Studio Projects. The proxies are updated whenever a change is detected in the AX object's schema.

A proxy enables your C# program to interact seamlessly with its corresponding AX object. Internally the proxy uses the .NET Business Connector for AX. You can see the code for a proxy by placing the cursor on an AX object and pressing F12 (or by selecting Go To Definition). You would see proxy code similar to the following:

public partial class PVsTable : Common {  // C#, code example 4.

    static string cTableName = "PVsTable";

// … code omitted for brevity …

    /// <summary>
    /// Dimension
    /// </summary>
    [ColumnAttribute()]
    public virtual string DimensionField {
        get {
            return ((string)(this.get_Field("DimensionField")));
        }
        set {
            this.set_Field("DimensionField", value);
            this.OnPropertyChanged("DimensionField");

            if ((DimensionFieldChanged != null)) {
                this.DimensionFieldChanged(this,
                    new System.ComponentModel.PropertyChangedEventArgs
                        ("DimensionField"));
            }
        }
    }
}

The proxy uses the business connector to talk to AX, by using the API that we investigated above. As you can see we are always relying on returning and setting the simple value, namely the first value. There are no provisions for getting the complete array, or even for explicitly identifying any individual array element of the DimensionField field. This is obviously unfortunate, since it hurts the C# interoperability story. However, all is not lost.

 

5.  Add Your Own Partial Class File to Extend the Proxy

The proxy class is declared as partial. Therefore you can add extensions by using your own partial class in the same project. In your added partial class, you add your own properties that access the DimensionField elements. There are at least two ways to accomplish this:

  • Add a property that returns the array
  • Add properties for the individual array elements

Below we show examples for both of these approaches.

 

5.1  Fetch an Array of All Elements

In the following C# code example, a string array of all the elements in the DimensionField field is fetched in one call:

 

public partial class PVsTable : Common {  // C#, code example 5.
       
    static string cTableName = "PVsTable";

// … code omitted for brevity …

    /// <summary>
    /// Dimension
    /// </summary>
    [ColumnAttribute()]
    [SuppressMessage("Microsoft.Design",
        "CA1062:ValidateArgumentsOfPublicMethods")]
    public virtual string[] DimensionFieldArray {
        get {
            return new string[] {
                (string)(this.get_Field("DimensionField[2]")),
                (string)(this.get_Field("DimensionField[3]")),
                (string)(this.get_Field("DimensionField[4]")),
                (string)(this.get_Field("DimensionField[5]"))
            };
        }
        set {
            // error diagnosis omitted for clarity…
            this.set_Field("DimensionField[2]", value[0]);
            this.set_Field("DimensionField[3]", value[1]);
            this.set_Field("DimensionField[4]", value[2]);
            this.set_Field("DimensionField[5]", value[3]);
 
            this.OnPropertyChanged("DimensionField");

            if ((DimensionFieldChanged != null)) {
                this.DimensionFieldChanged(this,
                    new System.ComponentModel.PropertyChangedEventArgs
                        ("DimensionField"));
            }
        }
    }
}

 

5.2  Fetch an Individual Element 

The other option is to get the individual elements by using explicit index values:

 

public partial class PVsTable : Common {  // C#, code example 6.

    static string cTableName = "PVsTable";

// … code omitted for brevity …

    /// <summary>
    /// Dimension
    /// </summary>
    [ColumnAttribute()]
    [SuppressMessage("Microsoft.Design",
        "CA1062:ValidateArgumentsOfPublicMethods")]
    public virtual string[] CostCenter {

        get {
            return new string[] {
                (string)(this.get_Field("DimensionField[2]")),
            };
        }
        set {
            // … error diagnosis omitted for clarity…
            this.set_Field("DimensionField[2]", value[0]);
            this.OnPropertyChanged("DimensionField");

            if ((DimensionFieldChanged != null)) {
                this.DimensionFieldChanged(this,
                    new System.ComponentModel.PropertyChangedEventArgs
                        ("DimensionField"));
            }
        }
    }

    // … And so on for Purpose, Project Type, and Project Group …
}

 You could even add a little value by using the names of the array elements to reference them in the DimensionField field.

 

 //End.