In AX, attributes are similar to .NET or java attributes. These are used to mark any method or class. To define a custom attribute, one must create a class that inherits from SysAttribute class. We create the class but we usually don’t have to instantiate the class. We will discuss in detail how to implement own attribute in X++.  Just like methods are defined to be called at another place, attributes are placed on methods/class to be used somewhere else in code. For example, there is a class that have several public methods and we may want to call methods in that class’ objects which have a specific attributes. There is also a possibility that we may define order at the time of applying attributes that those methods will be called in.

Below is an example of using attributes:

[DataMemberAttribute(‘Gender’)]

public Gender parmGender(Gender _gender = gender)

{

gender = _gender;

 

return gender;

}

 

Above code is an example of an attribute on a method. This code snippet is taken from a contract class of SSRS report. This method is tagged with ‘DataMemberAttribute’(Tag is an alternate term for attribute which is often used informally) to let SSRS know that there is a data member ‘Gender’ in the contract.

How to create an attribute?

Consider a scenario, where we have SomeProcess class that performs some operation. That process is split into different tasks as methods. While calling, we want the method sequence to be controlled with the attributes we will define on our processing class. First we will create an attribute class. As we discussed, we will have to inherit our attribute class with SysAttribute class.

class TaskOrderAttribute extends SysAttribute

{

int position;

}

 

Override new() method to take position as parameter and set it with private variable position

public void new(int _position)

{

super();

 

position = _position;

}

 

 

Also add a getter for position

public int getPosition()

{

return position;

}

 

Our attribute class is done. Next we have to put those attributes to our process class.

[TaskOrderAttribute(2)]

public void taskX()

{

info(“Task X”);

}

 

[TaskOrderAttribute(3)]

public void taskY()

{

info(“Task Y”);

}

 

[TaskOrderAttribute(1)]

public void taskZ()

{

info(“Task Z”);

}

 

According to the attributes we have set in the method, the calling order should be taskZ(), TaskX() and then TaskY().

Now we have to write develop code to call methods of these classes in a sequence which is defined in attribute. Here, we will use reflection. Following is the code for a job that will execute all methods.

static void runProcess(Args _args)

{

DictMethod  dictMethod;

DictClass   dictClass;

int         methodIndex;

MethodInfo  methodInfo;

Map         mapMethod;

TaskOrderAttribute  taskOrderAttribute;

SomeProcess         process;//Object of our process class that have different tasks defined as methods

 

mapMethod = new Map(Types::Integer, Types::String);//Using map here to keep method calling in order defined while tagging.

 

methodIndex = 1;

dictClass = new DictClass(className2Id(classStr(SomeProcess)));

 

while (methodIndex <= dictClass.objectMethodCnt())

{

dictMethod = new DictMethod(UtilElementType::ClassInstanceMethod, dictClass.id(), dictClass.objectMethod(methodIndex));

taskOrderAttribute = dictMethod.getAttribute(classStr(TaskOrderAttribute));

 

//Check to see if the attribute was found or not on the current method being traversed

if (taskOrderAttribute != null)

{

if (!mapMethod.exists(taskOrderAttribute.getPosition()))

{

//Insert the method name into the map with the position defined while taggin as a key

mapMethod.insert(taskOrderAttribute.getPosition(), dictMethod.name());

}

}

 

methodIndex++;

}

 

process = new SomeProcess();

 

methodIndex = 1;

 

while (mapMethod.exists(methodIndex))

{

//Calling all methods of our class SomeProcess that exist in the mapMethod

//Since methodIndex will go serially from 1 till last method found, our calling will also be sequential(as per tagging)

dictClass.callObject(mapMethod.lookup(methodIndex), process);

 

methodIndex++;

}

}

 

Running the code above will result in following infolog:2