Create Your First Custom Service [AX 2012]

As AX has advanced, so has its integration with WCF. One of the good concepts that MS has introduced in AX 2012 is custom services.

With custom services, anything and everything in AX can be exposed as Windows Communication Framework (WCF) service. This article concentrates on helping you create your first custom service.

For creating and exposing custom service, following steps are involved:

  1. Create a DataContract class (If required. In this example we will skip this step. I will include this in next article)
  2. Create a custom service class that utilizes the DataContract class and contains the required Business Logic
  3. Create a new Service utilizing service class
  4. Add the Service to service group
  5. Deploy the service group
  6. Verify the deployment ports
  7. Consume in Visual Studio (C#)

Let us see how we can achieve this.

Simple Scenario: The scenario we will take in this examples is customers. We will write a service to fetch all customers from default company. Add these customers to a list. Now select a subset of customers, pass it back to Ax and get their names. With this we can see how we can set and get an array of values.

Create Custom Service Class

Do the following:

  • In AOT –> Classes node, right click and create new class
  • Name it as SamCustomServiceTest

/// <summary>

/// Custom service class to demonstrate the creation and consumption of WCF services from AX

/// </summary>

/// <remarks>

/// This is the custom service class for fetching customer ids and customer names

/// </remarks>

class SamCustomServiceTest

{

}

 

  • Add a service operation method to retrieve customer ids as shown below

/// <summary>

/// Gets a list of customer ids from the specified company

/// </summary>

/// <returns>

///  List of customer Ids

/// </returns>

/// <remarks>

/// AifCollectionTypeAttribute is used to define strongly typed containers for data

/// </remarks>

[AifCollectionTypeAttribute(‘return’, Types::String), SysEntryPointAttribute(true)]

public List retrieveCustomerIds()

{

    List resultSet = new List(Types::String);

    CustTable   custTable;

 

    while select custTable

    {

        resultSet.addEnd(custTable.AccountNum);

    }

 

    return resultSet;

}

 

  • Add a service operation method to retrieve a combination of “Customer Ids and names” for the supplied list of customers as shown below

/// <summary>

/// Gets a list of customer ids from the specified company

/// </summary>

/// <param name=”_custList”>

/// The list of customer ids; Mandatory

/// </param>

/// <returns>

///  List of customer Ids

/// </returns>

/// <remarks>

/// AifCollectionTypeAttribute is used to define strongly typed containers for data

/// </remarks>

[SysEntryPointAttribute(true),

 AifCollectionTypeAttribute(‘return’, Types::String),

 AifCollectionTypeAttribute(‘_custList’, Types::String)]

public List retrieveCustomerNames(List _custList)

{

    ListEnumerator  listEnum = _custList.getEnumerator();

    List resultSet = new List(Types::String);

    CustTable   custTable;

 

    while (listEnum.moveNext())

    {

        select firstOnly custTable

            where custTable.AccountNum == listEnum.current();

 

        if (custTable)

        {

            resultSet.addEnd(custTable.AccountNum + “: “+ custTable.name());

        }

        else

        {

            resultSet.addEnd(listEnum.current() + “: Customer not found”);

        }

    }

 

    return resultSet;

}

 

imageNote: There are many metadata attributes specifed here. The first one is the AifCollectionTypeAttribute. This class has been developed to create strongly typed collections for data that you need to communicate. Here the return type if List (A collection object). AifCollectionTypeAttribute defines the type of data the list holds. When this service operation is exposed, the return type for the operation will be corresponding data type array (in this case a string array). The SysEntryPointAttribute indicates what authorization checks are performed for a method that is called on the server. This attribute must be set for all service operations. Value ‘true’ indicates authorization checks are performed on the calling user for all tables accessed by the method. Value ‘false’ indicates authorization checks are not performed on any tables accessed by the method. (You may have noticed that processReport method for any Data Provider class has this value set to false).

imageThe service operations should not be named as create, read, update delete, find etc. These names are exclusively reserved for methods on AIF Service classes. If you use these method names in your custom service, you will face errors while deploying them.

Create Custom Service

  • In AOT –> Services Right click and add new service
  • Name the service as SamCustomService, ExternalName = SamCustomService and specify Class = SamCustomServiceTest

image

  • Expand the newly created service node.
  • Right click on operations and select “Add service operations”
  • This opens an intermediate form, select both the service operation methods

image

Save the service and your service is ready now.

image

Create and Deploy Service Group

  • In AOT –> Service Groups Right click and add new service group
  • Name the group as SamCustomServiceGroup, Description = “Custom services for test” and AutoDeploy = Yes (Starts this service every time that the AOS is restarted)
  • Save the service group and then right click and Select “New Service Node Reference”
  • Type Name = SamCustomService and Service = SamCustomService
  • Save the Service group
  • Right click and select “Deploy Service Group”

image

Verify the Deployment

  • Open the Inbound ports form from System Administration –> Setup –> Services and Application Integration Framework –> Inbound ports.
  • Ensure that the SamCustomServiceGroup has a green check mark next to it. This means the port is active. If it is not, select SamCustomServiceGroup and then click Activate to activate the basic port.
  • You an use IE to view the WSDL file at the address specified in WSDL URI field of the Inbound ports form.

image 

Consuming Custom Service in Visual Studio (C#)

Now that the custom service is ready, let us go ahead and use that in a .Net Application. For that open your Visual Studio 2008 or 2010 development environment and do the following

  • Create new project of Type “Windows Application” for language Visual C#

image

  • Add a new form and then add two buttons and two List boxes as depicted below

image

  • In the solution explorer for the new project, select Service References node and right click. Click on “Add Service Reference”
  • In the subsequent dialog box set following: Address = WSDL URI from inbound port
  • Click on Go. This will display the available services from the port
  • Select SamCustomService and set Namespace = SamCustomAXService
  • Click Ok

image

image

  • Now double click on first button “Get Customers” and add following code there

private void button1_Click(object sender, EventArgs e)

{

    SamCustomAXService.SamCustomServiceClient servClient = new SamCustomAXService.SamCustomServiceClient();

    string[] custIds = servClient.retrieveCustomerIds(new SamCustomAXService.CallContext());

    listBox1.Items.Clear();

    foreach (string custId in custIds)

        listBox1.Items.Add(custId);

}

 

  • Now double click on second button “Get Select Cust Names” and add following code

private void button2_Click(object sender, EventArgs e)

{

    SamCustomAXService.SamCustomServiceClient servClient = new SamCustomAXService.SamCustomServiceClient();

    string[] strItem = null;

    int i = 0;

    strItem = new string[listBox1.SelectedItems.Count];

    //Get selected customer ids and prepare a string array

    foreach (Object selecteditem in listBox1.SelectedItems)

    {

        string item  = selecteditem as string;

        strItem[i++] = item;

    }

 

    //Use the string array to get the “Customer Id: Customer Name” Combo data

    string[] custIds = servClient.retrieveCustomerNames(new SamCustomAXService.CallContext(), strItem);

           

    listBox2.Items.Clear();

    foreach (string custId in custIds)

        listBox2.Items.Add(custId);

}

 

  • Now run the application, click first button “Get Customers”. The first list box fills up, then select some customers and click second button “Get Select Cust Names”. The second list box fills up as well

image

This concludes the session on creating custom services. To conclude, this is something really cool that MS has introduced in AX. Happy servicing the AX guys Laughing out loud. Next stop Data Contracts in Services.

42 thoughts on “Create Your First Custom Service [AX 2012]

  1. Nice post sumit. SamCustomServiceTest should be run on Server i believe, if i am not wrong it is just missed in above mentioned steps.

  2. I followed each step as stated here, however, when I execute the Program made through C#, I encountered the ff. error:

    System.ServiceModel.FaultException ’1[System.ServiceModerl.ExceptionDetails] : Object reference not set to an instance of an object. (Fault details is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is System.NullReferenceException: …………

    1. I have been dealing with a similar issue these days.

      In a Ax2012 CU1 I deployed a basic service for a table and generated a basic inbound port for it; then we moved that App to Ax2012 FP; then, we had to test it from VS and it always gave me the exception you say (creating, reading or writing records). A lot of struggling, reading, googling and debugging finally show me where the error was. [Oh my, so many hours spent for four single code lines generated by standard…]

      Look into the Interface class that works with the classes representing your tables; in the find[table], new[table],write[table],etc methods, look at the first line in variable declaration:

      [SysEntryPointAttribute(true)]
      public ExtProdBOM_IntData writeExtProdBOM(ExtProdBOM_IntData _extProdBOMData)
      {
      //JBExtProdBOM_IntData ret <- OLD CODE CREATED BY STANDARD
      JBExtProdBOM_IntData ret = new JBExtProdBOM_IntData();
      ;
      this.setLastOpStatus_Initial();

      ttsBegin;
      extProdBOM = JBExtProdBOM::find(_extProdBOMData.ProdId(), _extProdBOMData.lineNum(), true);
      extProdBOM.initFromExtProdBOM_IntData(_extProdBOMData);
      extProdBOM.write();
      ttsCommit;

      ret.initFromExtProdBOM(extProdBOM); <- This issues the break with old code… ret is null if not new()'ed before

      this.setLastOpStatus_OK();

      return ret;
      }

      modify the proper line with the new [tableInterfaceClass] and it should work. I think in FP and later this bug (these classes were auto-created by AIF, not me) has been solved. I have noticed that http protocols and authentications works much better in Ax2012 FP than in earlier versions of Ax2012.

  3. This would be really useful to me … except that nothing seems to compile!
    When I try to compile the class it gives Err:9999 Syntax Error and always underlines the attributes (between [] brackets) with curly red lines.

    Also, if I try to enter 3 ‘/’ characters adjacent to each other as in your comments, I immediately get a STOP error. Two ‘/’ characters works fine!

    I am completely new to this. Are there any ‘header’ files or other environmental issues which may be causing my problems?

    Thanks for any hints!

  4. Ah! I of course just realised that the three /// characters are C# syntax, and I was attempting to replicate this in a dev window of the Ax client – ie in MorphX (X++). Are you using Visual Studio to generate the Custom Service Class?

  5. Thanks Sumit – I actually realised this shortly after I posted! I also notice that if I cut/paste your code into my dev window then it lets the XML comments through, but then objects to the /// lines and also the [attribute] statements as before. This is very strange and very frustrating!

    If I type in “//”, it’s happy until I type the 3rd ‘/’ when it immediately produces a STOP error saying “Found 1 compile error”. if I simply hit carriage return after one of the lines inserted via pasting, it automatically inserts the “///” for me with no complaint!

    Sorry – I know now this is not your problem nor the correct place to discuss it. I’ll return when I can actually enter code into a DEV window without any problems!

    Thanks

    Ade

  6. OK, problem resolved!

    After re-installing Ax on the Server, I could suddenly type XML documentation comments into the design window. The first ‘///’ produced the full template for summary, remarks etc, as expected.

    I then read some more around the MorphX dev environment and X++ in general, and discovered the use of “-n” to add new methods! Being a complete novice with X++, and it being so similar to C# etc, I made the incorrect assumption that when in your commentary you said “Add a service operation method to retrieve customer ids” etc, that I had to add the code WITHIN the original class declaration, as you would in C#. Of course, this is incorrect!

    As soon as I discovered the use of ctrl-n and added the methods this way, everything started working!

    Thanks again.

    Ade

  7. PS There is some formatting wizard re-formatting my text. XML-tag type text in my original posts have been removed (eg the summary and remarks tags where I entered them surrounded by angle-brackets) and also, mysteriously, the ctrl text within quotation marks. Where my message says -n it should read ctrl-n!

  8. Also, don’t forget to modify the ListBox.SelectionMode property of the first listbox so you can select multiple customer accounts.

  9. Hi. Thank you for your nice post. Can you tell me how to use your CreateCustomer operation service to do an import of XML file by using a file system adapter in AX 2012

  10. Sumit:

    Can this custom service used for crossing different domain?
    if so, where can I provide the domain information like domain name and log in ID and password?
    Thank you
    Jeffery

  11. Sumit:
    We figure out that AOS host WCF can work for cross domain when VPN is build up.
    just as reference if anyone interested: after this line
    SamCustomAXService.SamCustomServiceClient servClient = new SamCustomAXService.SamCustomServiceClient();

    // you can connect with domain name by providing

    if (servClient != null)
    {
    servClient.ClientCredentials.Windows.ClientCredential.Domain = “domainName”;
    servClient.ClientCredentials.Windows.ClientCredential.UserName = “yourID”;
    servClient.ClientCredentials.Windows.ClientCredential.Password = “yourPassword”;
    servClient.Open();
    }
    // you can also retrieve data for certain company inside AX by providing this
    SamCustomAXService.CallContext callContext= new SamCustomAXService.CallContext();
    callContext.Company = “AXDataAreaid”;
    callContext.logonAsUser = “AXloginID”;

    In this case, You can have AX WCF service running under A domain and have C# apps call it from B domain.

    Best Regards
    Jeffery Yu

  12. Sumit,

    How do I trigger a service class to send data from AX on creation of a new record in Customer form
    could you please let me know is there a way to trigger the service class.

    thanks
    Sangeeta

  13. File system adapter. I will create one custom service to send the customer details when a new customer got created in the system.

    1. Why dont you use standard Customer service AIF? You can utilize the AIF framework and invoke the AIF service during insert and update to submit an asynchronous request to AIF

  14. Hi Sumit,
    In Ax custom service deployed successfully & using the browser i m able to reach to the service
    but when from visual studio I am trying to call the service getting error Call to SSPI failed.see inner exception…Can u plz help..

  15. Hello Sumit,
    I have created a service for custTable Query through Wizards. I added Reference in VS. now i want to create new customer through C# project. Can u Suggest me How shall i Do it?
    Thank you

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s