In this section, we will discuss two custom services. One service focuses on exposing data from Microsoft Dynamics AX 2012 while the other focuses on exposing business logic.
We will use the CVRTitleService
service as an example to demonstrate how to create a simple yet powerful service. This service will allow an external program to do the following two things:
Let's start by creating a new class for the data contract that will contain the data for one title. Create a new class and name it CVRTitleContract
. In the class declaration, add DataContractAttribute
to specify that the class is a data contract. Also, declare the variable's ID, name, and description, as shown in the following code snippet:
[DataContractAttribute('Title')] public class CVRTitleDataContract { CVRTitleId id; CVRTitleName name; Description description; }
Next, add three parameter methods, one for each of the properties of the data contract. Use DataMemberAttribute
to indicate that the methods are data contract members, as shown in the following code snippet:
[DataMemberAttribute('Description')] public Description parmDescription(Description _description = description) { description = _description; return description; } [DataMemberAttribute('Id')] public CVRTitleId parmId(CVRTitleId _id = id) { id = _id; return id; } [DataMemberAttribute('Name')] public CVRTitleName parmName(CVRTitleName _name = name) { name = _name; return name; }
As you can see in the preceding code, we construct the attributes using an optional string parameter. This parameter is the name. Because we do that, a client application can get the value of a member using code such as title.Description
. If we don't pass a name, the client application would have to use CVRTitleDataContract.parmDescription
instead, which doesn't look as neat. It's better to not expose the prefixes and other naming conventions that are specific to Microsoft Dynamics AX such as the DataContract
suffix and parm
prefix.
Essentially, you now have a functional data contract. However, there are a few tweaks that we can still perform when constructing the data contract. Because our contract is tied to a record of the CVRTitle
type, we can create a static new()
method that creates an instance of the data contract based on a record of this type. Note that these steps are optional, but performing them has the following main advantages:
new()
and construct()
methods are not publicly available. This way, a developer who creates an instance of the contract is less likely to make mistakes.new()
method. This will make your code cleaner and easier to understand.Start by overriding the new()
method and set it as protected
so only the CVRTitleDataContract
class or one of its subclasses can call the method, shown as follows:
protected void new() { }
Always create a construct()
method for your classes, but if it doesn't return a valid instance, set it as private
. A valid instance means that when constructed, all of the variables needed for execution have to be initialized. Creating an instance of the data contract using the construct()
method isn't valid in this case because the properties id
, name
, and description
are not set:
private static CVRTitleDataContract construct() { return new CVRTitleDataContract(); }
Finally, create a static new()
method that takes a CVRTitle
record as a parameter, uses it to construct an instance of the CVRTitleDataContract
class, and returns it:
public static CVRTitleDataContract newFromTableRecord(CVRTitle _title) { CVRTitleDataContract contract = CVRTitleDataContract::construct(); contract.parmId(_title.Id); contract.parmName(_title.Name); contract.parmDescription(_title.Description); return contract; }
Best practices
These recommendations are based on the best practices defined by Microsoft at http://msdn.microsoft.com/en-us/library/aa854210.aspx.
So there you go, you have created your first data contract. That wasn't too hard, was it? Now, let's see how we can create a list data contract, which is a little more complex.
We will create a list data contract using the data contract that we just created by performing the following steps:
CVRTitleListDataContract
. Add the DataContractAttribute
attribute to it to declare that the class is a data contract and add a list variable that will store a list of titles, shown as follows:[DataContractAttribute] public class CVRTitleListDataContract { List titleList; }
new()
and construct()
. Also, don't forget to initialize the list object, shown as follows:protected void new() { titleList = new List(Types::Class); } public static CVRTitleListDataContract construct() { return new CVRTitleListDataContract(); }
public void addTitleToList(CVRTitleDataContract _titleDataContract) { titleList.addEnd(_titleDataContract); }
DataMemberAttribute
attribute as you would for every other data member, but also add two more attributes of the type AifCollectionTypeAttribute
, as shown in the following code snippet:[DataMemberAttribute ,AifCollectionTypeAttribute('return', Types::Class, classstr(CVRTitleDataContract)) ,AifCollectionTypeAttribute('_titleList', Types::Class, classstr(CVRTitleDataContract))] public List parmTitleList(List _titleList = titleList) { titleList = _titleList; return titleList; }
As we've discussed previously, the AifCollectionTypeAttribute
attribute is used to specify the type of the list, because X++ does not support strongly typed lists. In this case, AifCollectionTypeAttribute
takes the following three parameters:
_titleList
. For the return value, the name is return
.Class
in this example.CVRTitleDataContract
.This concludes the creation of the two contracts that we will need for our service. Now let's see how we can use them.
We will create a service class that has the following two service operations:
First, we create a service class. Create a new class and name it CVRTitleService
, as shown in the following code snippet. We do not need to add anything more to the class declaration because a service class declaration does not need an attribute:
public class CVRTitleService { }
One thing we have to make sure is that this class runs on the server when it is executed. To do this, right-click on the class, click on Properties, and then set the RunOn property to Server.
OK, let's create a service operation that retrieves the details of a title based on its ID. Start by creating a new method. You can see the source code of this method in the following snippet. As you can see, we add the SysEntryPointAttribute
attribute to specify that the method is a service operation. We add true
between brackets when constructing the attribute to specify that the AOS authorization has to be performed when the code runs on the server. This will help you make sure that the user who calls the service operations has the necessary permissions on the tables that the method uses:
[SysEntryPointAttribute(true)] public CVRTitleDataContract getTitle(CVRTitleId _titleId) { CVRTitleDataContract contract; contract = CVRTitleDataContract::newFromTableRecord(CVRTitle::find(_titleId)); return contract; }
As you can see, further in the method, we use the _titleId
parameter to find the record in the database and construct a new data contract with it. Then, we return the data contract.
This service operation will use the list data contract to return a list of all the titles. As you can see in the following code, all titles in the CVRTitle
table are traversed. Then, a data contract is constructed and added to the list contract. Finally, a list contract that contains the details of all of the titles is returned:
[SysEntryPointAttribute(true)] public CVRTitleListDataContract getAllTitles() { CVRTitleListDataContract titleListDataContract = CVRTitleListDataContract::construct(); CVRTitleDataContract titleContract; CVRTitle titleRecord; while select titleRecord { // Convert the record to a data contract titleContract = CVRTitleDataContract::newFromTableRecord(titleRecord); // Add the title data contract to the list of data contracts titleListDataContract.addTitleToList(titleContract); } return titleListDataContract; }
The final thing we have to do before we can deploy our service is define the service contract. To create the service contract, perform the following steps:
CVRTitleService
.http://schemas.contoso.com/ServiceContracts
.18.224.54.136