AX2012: SysOperation part 1: Data Contracts and Service Operations

August 22, 2011 at 12:00
filed under Dynamics AX
Tagged , , ,

Hi All :-)

This is part 1 of a series of 3 blog posts where I demonstrate how to create batch classes using the SysOperation classes (Read the introduction here)

We are going to create a batch that looks like this:

And we are going to do that without creating a RunBaseBatch class!

Note: Just to be clear: the batch class won’t actually do anything useful, everything is just for demo purposes.

Okay, let’s start!

1. Data Contract

First, we will create a data contract. The data contract contains data members that represent the fields that will be available on the dialog.

Let’s create a new class, KlForCustTesterDataContract:

[DataContractAttribute]
class KlForCustTesterDataContract
{
}

The string DataContractAttribute indicates that this class is a Data Contract. The square brackets [] indicate that this string is an attribute (You can read more about attributes on MSDN).

Next, let’s add Data Members to this contract by first declaring them in the class declaration

[DataContractAttribute]
class KlForCustTesterDataContract
{
    Name        name;
    TransDate   transDate;
    str         packedQuery;
}

We add a string field, a date field, and a query. We still need to create parm methods for these variables.

This is just a simple string field, nothing special here except for the DataMemberAttribute attribute that indicates that this method is da data member.

[DataMemberAttribute]
public Name parmName(Name _name = name)
{
    name = _name;
    return name;
}

The date field is of type TransDate, so we’ll give it a nicer label by using the SysOperationLabelAttribute attribute to specify the label used.
Also note that, when two attributes are specified, the are separated by a comma (,).

[DataMemberAttribute
,SysOperationLabelAttribute(literalStr("@SYS11284"))] // today's date
public TransDate parmTransDate(TransDate _transDate = transDate)
{
    transDate = _transDate;

    return transDate;
}

We will also add a query, so we can loop all customers. As you can see, we can specify the query by using the AifQueryTypeAttribute. The query KlForCustomers is just a query in the AOT with CustTable as a datasource (see xpo file at the end).

[DataMemberAttribute,
    AifQueryTypeAttribute('_packedQuery', querystr(KlForCustomers))
]
public str parmQuery(str _packedQuery = packedQuery)
{
    packedQuery = _packedQuery;
    return packedQuery;
}

Next, we add two helper methods that are not Data Members, so we can easily set and get the query variable.

To get the query:

public Query getQuery()
{
    return new Query(SysOperationHelper::base64Decode(packedQuery));
}

To set the query:

public void setQuery(Query _query)
{
    packedQuery = SysOperationHelper::base64Encode(_query.pack());
}

2. Service class and operation

Next, let’s create a service class called KlForCustTesterDataService.

class KlForCustTesterDataService
{
}

Right click the class, click properties, and set the run on property to server.

Adding a service operation to this service class is as simple as adding a method. As you can see, the SysEntryPointAttribute attribute indicates that this is a service operation, and our data contract is used as an argument.

[SysEntryPointAttribute]
public void testCustomer(KlForCustomerTesterDataContract _klForCustomerTesterDataContract)
{
}

Of course, this method needs some demo functionality, so let’s add that:

[SysEntryPointAttribute]
public void testCustomer(KlForCustTesterDataContract _klForCustTesterDataContract)
{
    QueryRun    queryRun;
    CustTable   custTable;
    ;
   
    // info the name parameter
    info(_klForCustTesterDataContract.parmName());

    // create a new queryrun object
    queryRun = new queryRun(_klForCustTesterDataContract.getQuery());

    // loop all results from the query
    while(queryRun.next())
    {
        custTable = queryRun.get(tableNum(custTable));

        // display the accountnum
        info(custTable.AccountNum);
    }
}

3. Service

Right click your project, and choose New Service. Give this service the same name as the service class, KlForCustTesterDataService.
In the properties of this node, set the class property to KlForCustTesterDataService
Expand the node of your service, then right click on the Operations node.
Click Add operation and check the add checkbox next to the method we’ve created, and click OK.

4. Menu item

All we need to do now is create a menu item so we can start our dialog.
Do this by right clicking your project – New Menu item. Name it KlForCustTesterDataService.

Enter the following properties:
ObjectType: Class
Object: SysOperationServiceController
Parameters: KlForCustTesterDataService.testCustomer

As you can see, we don’t link directly to our service class, in stead, the class SysOperationServiceController will be used, and will know what service operation to start because it is specified in the parameters property. More about these controller class in part 2 of this series.

5. Run it!

But wait, we still have to do one small thing: compile our X++ into the common intermediate language (CIL). Don’t worry, it’s easy. Just click the Generate Incremental CIL button in the toolbar (this might take a while).

After CIL was generated, right click the menu item and click Open. You should see your batch class. As you can see, all fields from the datacontract are displayed, and a select button was automatically generated so you can modify the query.

Output when run in batch:

In part 2, we will take a closer look at the SysOperationServiceController class. See you tomorrow.

Download the XPO for part 1 here.

17 comments

RSS / trackback

  1. Prashanth

    Hi,

    All your posts are very help full to understand the AX 2012..

    I have a query..

    Read the data from a service (Non AX.. ex: TFS)
    And sync the data to Dynamics AX 2012 table.

    To achieve this
    1) Create AX inbound service which will receive the parameters and insert the data into AX table
    2) Create a .net console application , read the data from the service and pass the data to AX 2012 through above created inbound port (Added as a reference)
    3) Add the DLL of the .net project to AOT and then consume the DLL by adding the .net project’s config file settings into AX32.exe config file.

    All above steps are correct except teh 3rd. We should not modify the AX32.exe

    Could you please let me know whether the approach used above is correct?

    Thanks in advance

  2. Klaas Deforche

    Hi Prashanth,

    I’m not sure if I understand correctly.
    Are you trying to consume an external service from an other application in AX?

    If so, I haven’t done this before in 2012, but there is a white paper on this available here: http://www.microsoft.com/download/en/details.aspx?id=24742 (Consuming_Web_Services_AX2012.pdf)

    You should be able to add a service reference in visual studio, then add it to the AOT and use the webservice from there (without the use of a console application or modifying the config file)

    Hope this makes sense and answers your question (when it doesn’t please rephrase :).

  3. Aman

    I am creating a batch job using Sys operation framework and i followed similar steps as you mentioned above with a service class, a data contract class, a service, a query and a menu item. The logic should create trade agreements, and for that i am using PricePriceDiscJourService. If i run the logic in a standalone class, it works fine. Records get created in all the right tables like PriceDiscAdmTrans, PriceDiscAdmtable, and PriceDiscTable. But if try to run the batch job (after CIL compile ofcourse), it doesnt create all the records. The query seems to skip few records. and it doesn’t makes sense.

  4. Klaas Deforche

    Hi Aman,

    That is weird. I would suggest you debug it using Visual Studio as described here:
    http://learnax.blogspot.be/2012/03/ax-2012-how-to-debug-code-in-visual.html

    Most code executes the same in XPP and CIL (I say most, because there are differences. eg year(mkDate(1, 1, 0)) will return different results depending on whether it runs in CIL or not).
    There are scenario’s that are not supported: http://msdn.microsoft.com/en-us/library/hh397320.aspx
    Also notice (see previous link) that sometimes an incremental CIL won’t do the job and a full CIL compile is needed.

    It could be that CIL is not properly compiled and that you are running ‘old’ code, you should see that immediately when debugging.
    When that is the case, do a full compile and full CIL.

  5. Aman

    I debugged the code in Visual Studio.
    I used a while(query.next()) to loop through two header/line tables and fetch records.

    The while loop works little differently in Visual Studio then in X++. The RecId of the line table gets reset when the while loop moves to the next record, which surprisingly is not the case in AX. AX keeps the RecId value until the line table gets reassigned the next table record. My logic depended on this and it failed. I changed the logic to check some field value other than RecId and it worked.

  6. GVK

    hi All,

    I m the new in Ax 2012,
    I m struck in one process.

    how to create the sysoperation frame work with out using data contract class(parameters).I need only scheduling the batch, I dont want pass the any parameters.

    please let me know any idea

  7. Klaas Deforche

    Hi GVK,

    At what point are you stuck?
    You should be able to create a service operation without a parameter (so no data contract) and use that.
    You should then see an empty first tab and be able to schedule a batch task by activation the batch tab.

    On my todo list: look for a way to hide that first tab as requested here too: http://www.artofcreation.be/2011/08/24/ax2012-sysoperation-part-3-sysoperationautomaticuibuilder/comment-page-1/#comment-18557

  8. pawan

    Hi Klaas Deforche,
    Im facing this error when im trying to do create a batch process please help me out..

    System.MethodAccessException: Attempt by method ‘Dynamics.Ax.Application.SysOperationServiceController.Getdatacontractinfoobjectsinternal()’ to access method ‘Dynamics.Ax.Application.SysOperationDataContractInfo.newParameterInfo(System.String, Microsoft.Dynamics.Ax.Xpp.XppObjectBase, Dynamics.Ax.Application.SysDictClass, Dynamics.Ax.Application.SysOperationDataMemberInfo, Boolean, Boolean)’ failed.

    at Dynamics.Ax.Application.SysOperationServiceController.Getdatacontractinfoobjectsinternal() in SysOperationServiceController.getDataContractInfoObjectsInternal.xpp:line 35

    at Dynamics.Ax.Application.SysOperationController.Initializedatacontractinfomaps() in SysOperationController.initializeDataContractInfoMaps.xpp:line 9

    at Dynamics.Ax.Application.SysOperationController.Unpackdatacontractinfoobjects(Object[] packedContracts) in SysOperationController.unpackDataContractInfoObjects.xpp:line 21

    at Dynamics.Ax.Application.SysOperationController.Unpack(Object[] pack) in SysOperationController.unpack.xpp:line 70

    at Dynamics.Ax.Application.SysOperationServiceController.Unpack(Object[] packedState) in SysOperationServiceController.unpack.xpp:line 15

    at Dynamics.Ax.Application.BatchRun.runJobStaticCode(Int64 batchId) in BatchRun.runJobStaticCode.xpp:line 36

    at Dynamics.Ax.Application.BatchRun.runJobStatic(Int64 batchId) in BatchRun.runJobStatic.xpp:line 13

    at BatchRun::runJobStatic(Object[] )

    at Microsoft.Dynamics.Ax.Xpp.ReflectionCallHelper.MakeStaticCall(Type type, String MethodName, Object[] parameters)

    at BatchIL.taskThreadEntry(Object threadArg)

  9. Klaas Deforche

    Hi Pawan, I think it’s a a problem with your CIL compilation. Do a full CIL and if that doesn’t work, do a full X++ compile and then a full CIL.

  10. Gurjeet

    Hi,

    I have followed the steps you have mentioned above But after opening the MenuItem and filling the fields, When I click on Ok an infolog arises after 5 minutes saying “Unable to monitor batch job for operation”. Kindly help me with same.

  11. Zoltan

    Hi Gurjeet,

    Were you able to work out this isse? If yes, would you be so kind to share the solution?

    Thanks,
    Zoltan

  12. Ashish

    after following all steps and clicking on incremental CIL and open menu item there is no dialog box getting displayed..only batch tab is displaying…please help me …

  13. mark

    Didn’t work. warning log says “Unable to monitor batch job for operation”

  14. Klaas Deforche

    Hi Mark,

    On your menu item, please specify the following properties:
    – EnumTypeParameter = SysOperationExecutionMode
    – EnumParameter = Synchronous

  15. Trackbacks

respond