Tuesday 8 November 2016

Query Service AIF AX2012


 AIF query service can be used to fetch ad hoc data from AX2012 without creating a new service.
Its a common service which can be used to retrieve the data from AX database. There are three types of queries that you can run through a query service but I am discussing only the first two options here:


In the below code I will demonstrate the use of query service for fetching all the records from the SalesTable through the user defined query. In order to run this snippet, please make sure that AIF services are deployed and you are able to access the query service.  URI for the query service WSDL is:

http://<AOS_HOST>:8101/DynamicsAx/Services/QueryService


C# code for fetching the records

QueryServiceClient client = new QueryServiceClient();
QueryMetadata queryMetaData = new QueryMetadata();
queryMetaData.DataSources = new QueryDataSourceMetadata[] {
new QueryDataSourceMetadata() { DynamicFieldList=true, Table=”StudentTable”,  Name=”StudentTable”,  Enabled=true}
};

Paging paging = null;

DataSet ds = client.ExecuteQuery(queryMetaData, ref paging);

foreach (DataRow dr in ds.Tables[0].Rows)
{
Console.WriteLine(dr[“PersonnelNumber”].ToString());
}

Console.ReadLine();


Sunday 6 November 2016

How to debug AIF AX2012

In this post I will demonstrate the debugging of AX2012 AIF services, which makes the lives of developers easy :).

We can simply debug the AIF service, triggered from C# code which was written to consume the AIF services.

I have created a document service and written some custom code in service classes. Now I want to debug and see the impact.

Follow below steps:

Prerequisite: Your environment should be full CIL compiled.

Step 1: Create a console application in visual studio C#, to consume the AIF service. This was demonstrated in my earlier post. For more details visit below link.


Step 2: Open a new visual studio application. Right click visual studio and Run as admin.

Step 3: Open Application explorer in visual studio and find your service class.




Step 4: Now go to Debug option and attach the process to server.
 Step 5: Make sure that the symbol loading is completed and the debugger symbol is completely turned Red.


Step 5: Now the service is ready to be debugged. Switch to the first visual studio application where you have written the C# code as per the Step 1. Make sure the two VS applications are running simultaneously.

Step 6:  Run the test code, the debugger will hit the line of code on another VS application where you have attached the server processes.


Note: Debugging is possible in VS only, this will not open AX debugger, also the client side code will not be executed in debugger.

Happy daxing :)


Wednesday 26 October 2016

How to create AIF AX custom service

In this post I will demonstrate the creation of custom service. Custom service is often designed or created for performing the business logic triggered from third party applications. In this post I will demonstrate that how a sales order confirmation can be triggered.

There are multiple scenarios where we need custom services to be implemented and this is one of the simple scenarios .

Basics


1. We need a contract class, contract class is specifically needed when the service deals with multiple parameters or if the parameters passed by third party application are user defined data types or collection types.

2. We need a service class to perform the business logic.

3. We need to create a Service from service node and then add the required service operation to service. Only the methods which were marked as SysEntryPointAttribute{true}, can be exposed to service.

4.Register the service. Service registration is need in case we need enhanced port. (Enhance port is covered in earlier posts)

5. Create a service group with the above service node and deploy the service group.

6. Write C# code to test the service.

Steps to be followed

Create custom service


1. Create contract class

[DataContractAttribute]
class SalesConfirmationContract
{
    SalesId salesId;
}

[DataMemberAttribute('SalesID')]
public SalesId parmSalesId(SalesId _salesId = salesId)
{
    salesId = _salesId;

    return _salesId;
}

2. Create service class
public class SalesConfirmationService
{
}

[SysEntryPointAttribute(true),
AifCollectionTypeAttribute('contract', Types::Class, classStr(SalesConfirmationContract))
]
public void  confirmSO(SalesConfirmationContract contract)
{
    SalesFormletter SalesFormletter;
    SalesTable      SalesTable;
    SalesId         salesId = contract.parmSalesId();

    try
    {
        ttsBegin;

        SalesFormletter = SalesFormletter::construct(DocumentStatus::Confirmation);
        SalesTable.clear();
        SalesTable = SalesTable::find(salesId);
        SalesFormletter.update(SalesTable,
                               systemDateGet(),
                               SalesUpdate::All,
                               AccountOrder::None,
                               false,
                               false);

        ttsCommit;
    }

    catch (Exception::CLRError)
    {
        throw error(CLRInterop::getLastException().ToString());
    }
}

3. Generate incremental CIL.

4. Create new service from service node and add the class to service created on step 3.


5. Right Click the operation node and add the service operation.
6. Right click on the service and register the service.

7. Create a service group and add the new service to this service group.



8. Right click and deploy the service group.

Test Custom service

1. Copy the WSDL from inbound port.

2. Create new console application in VS.

3. Add service reference.


4. Copy and paste below code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConfirmSO.SalesSR;
namespace ConfirmSO
{
class Program
{
static void Main(string[] args)
{
try
{

SalesConfirmClient client = new SalesConfirmClient();
CallContext context = new CallContext();
context.Company = "EAM";
context.Language = "en-au";
SalesConfirmationContract contract = new SalesConfirmationContract();
contract.SalesID = "SO-000001";
client.confirmSO(context, contract);

Console.WriteLine("Sales order confirmed");
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Console.ReadLine();
}
}
}
}

5. Build and Run the VS C# code in order to confirm the sales order. 



Happy daxing... :)









        1. Tuesday 25 October 2016

          How to create and test the DLL of AX2012 service

          This post will demonstrate the creation of  DLL with AX2012 service and than testing the DLL from Visula studio.

          Hi guys this requirement is not very common and I could hardly found any post which share the details about this. I did a POC for a requirement where the third party software was not enough capable to directly consume the AX2012 SVC.

          In order to provide a solution we created DLL out of the AX2012  service and than tested the same DLL using as a reference in visual studio.

          Step: 1 Create DLL for your existing service.
          Goto Visual Studio --> New --> Project --> C# --> ClassLibrary --> Add service reference
            
          Step 2: Set your project to release mode.


          Step 3: Build the project and copy the dll from release folder.
          Release folder path
          Step 4: Go to config in same VS project and copy the endpoint address and save it for later use.
          Now close the VS solution. Your DLL is ready to be used copy this dll to any other location on your system.

          Step 5: To test the same dll we need to create another VS solution.
          Goto --> File --> New --> Projects --VisualC# --> ConsoleApplication.
          Step 6: Add reference to you dll.
          Goto --> Solution Explorer --> Reference --> Right click --> Add new reference
          Step 7: Browse to the dll folder path and select dll file to be added.
          Step 8: Repeat step 6 and add one more service reference from Assembly.

          Step 9: Add below code to your console application
          using System;
          using System.Collections.Generic;
          using System.Linq;
          using System.Text;
          using System.Threading.Tasks;
          using StudentTableDll.StudentService;
          using System.Configuration;
          using System.ServiceModel;
          namespace TestDLLDemo
          {
          class Program
          {
          static void Main(string[] args)
          {
          EndpointAddress ep = null;
          System.ServiceModel.NetTcpBinding tcpb = new System.ServiceModel.NetTcpBinding();
          System.ServiceModel.ChannelFactory channelFactory = new System.ServiceModel.ChannelFactory<StudentTableService>(tcpb);

          // End Point Address taken from step 4 in same blog

          string strEPAdr = "net.tcp://ax2012r3dev:8201/DynamicsAx/Services/StudentTable";
          ep = new EndpointAddress(strEPAdr);
          StudentTableServiceClient client = new StudentTableServiceClient(tcpb, ep);
          AxdStudentTable axd = new AxdStudentTable();
          CallContext context = new CallContext();
          context.Company = "EAM";
          context.Language = "en-au";
          AxdEntity_StudentTable studentTable = new AxdEntity_StudentTable();
          studentTable.Name = "Kumar";
          studentTable.Standard = "Eight";
          studentTable.RollNumber = 01;
          axd.StudentTable = new AxdEntity_StudentTable[1] { studentTable };
          try
          {
          EntityKey[] returnFloc = client.create(context, axd);
          EntityKey returnedFloc = (EntityKey)returnFloc.GetValue(0);
          Console.WriteLine("the record has been created RECID- " + returnedFloc.KeyData[0].Value);
          Console.ReadLine();
          }
          catch (Exception e)
          {
          Console.WriteLine(e.ToString());
          Console.ReadLine();
          }
          }
          }
          }

          Step 9: Before running the code please ensure the below highlighted points.


          Step 10: Run to code to create the record in student table.

          Happy daxing :)

          Monday 24 October 2016

          AIF AX 2012  Document service operations

          Basically we deal with normal service (CRUD) operation while creating a document service. I hope everyone who came to this post might be aware of basic CRUD operations. Hence, I will explain them in brief whereas will try to focus on other operations available in AX 2012 document service and will be more useful.

          Create: Create operation will insert the record as per the document service query. It performs create operation on all the related data sources  which are related in query if specified.

          Read: Read operation read the document based on passed entity key and return the AXd class instance which contains information about all the related datasets.

          Update: There is often requirement to update the record from third party applications, hrnce need to use update operation in such scenarios.

          Delete: Delete operation has been rarely used but, is called when we need to delete the record when instructed by third party application.

          Find: Find operation works similar as it is in X++, it will try to find the record based on criteria specified for find operation and return the record.

          findKeys: This operation is similar to find operation where as it is different in terms of return value, it return only the entity keys required, whereas find returns the complete record. This operation is used when the service is deployed using enhanced port only.




          Code to trigger outbound service AX AF 2012

          We often get the requirement to export the the xml file for a particular document using outbound port. Outbound port can be triggered normally through AIF gateway classes in batch. Further their are requirements where we may need to generate the XML  using outbound port on any particular event.

          Below is the code which can be placed on any event handler to trigger a outbound port configured for the XML document.

          Sample X ++ code:

          public client server static void OutboundFileSytemAdapterInvokeJob(XppPrePostArgs _args)
          {
            SalesTable              salesTable;
            AxdSendContext          axdSendContext = AxdSendContext::construct();
            Map                     keyData;
            ClassId                 serviceClass;
            AifConstraint           aifConstraint = new AifConstraint();
            AifConstraintList       aifConstraintList = new AifConstraintList();
            AifActionId             actionId;
            AifEndpointList         endpointList;
           
            AIFGatewaySendService   aIFGatewaySendService = new AIFGatewaySendService();
            AifEntityKey            aifEntityKey  = AifEntityKey::construct();
            #define.xmlServiceRead('SalesSalesOrderService.read')
           
           
               
            salesTable= _args.getThis();
           
            if(salesTable.RecId)
            {
             keyData = SysDictTable::getKeyData(salesTable);
             aifEntityKey.parmTableId(salesTable.TableId);
             aifEntityKey.parmRecId(salesTable.RecId);
             aifEntityKey.parmKeyDataMap(keyData);
             axdSendContext.parmXMLDocPurpose(XMLDocPurpose::Original);
             axdSendContext.parmSecurity(false);
             serviceClass = className2Id(classStr(SalesSalesOrderService));
             aifConstraint.parmType(AifConstraintType::NoConstraint);
             aifConstraintList.addConstraint(aifConstraint);
             if(serviceClass)
             {
              //Get the actionId of the default action for this document class
              actionId = AifSendService::getDefaultSendAction(serviceClass, AifSendActionType::SendByKey);
              actionId =  xmlServiceRead;
           
              if(actionId)
              {
               //Get the list of valid Endpoints
               endpointList = AifSendService::getEligibleEndpoints(actionId, aifConstraintList);
               AifSendService::submit(actionId,endpointList,aifEntityKey,AifSendMode::Sync,axdSendContext.pack(),AifProcessingMode::Parallel);
              }
             }
             AIFGatewaySendService.run();}
           }
          }
          
          AIF AX2012 good to know concepts.

          In this post I am trying to consolidate the concepts which are good to know and can help in making the right decision for deciding the approach of service creation and customization of existing ones.

          1. Axd class methods.

          • getSurrogateForeignKeyValue() : This method is used for getting the surrogate key values for related table fields. Below is the example code snippet.

          public RecId getSurrogateForeignKeyValue(AxdBaseProperty _axdBaseProperty, Struct _sfkReplacementValue)
          {
          RecId ret;
          ret = super(_axdBaseProperty, _sfkReplacementValue);
          switch (_axdBaseProperty.parmImplementingTableId())
          {
          case tableNum(DirpartyTable) :
          switch (_axdBaseProperty.parmImplementingFieldId())
          {
          case fieldNum(DirpartyTable, Name):
          ret = DirpartyTable::find(_sfkReplacementValue.value(fieldStr(DirPartyTable,Name))).RecId;
          spTypeId = ret;
          break;
          }
          }
          return ret;
          }
          .
          This method will be overridden in scenarios where the document service has a table which has multiple foreign key relations. As we know that we cannot pass the recId values directly, but certainly we may need to populate the foreign key values. Hence we can override this method and finds the related recId of the value passed for certain field.
          sfkReplacementValue.value denotes the replacement key value mentioned on the related table.
          • UpdateNow(): This method is the last point  of execution in document service and is called during the create and update operations. This method is basically used when we have to perform any business logic on the created or updated entity. for ex: If I am using a document service to create a sales order in AX and my requirement is to confirm the same sales order while creation only. In this scenario I can write my business logic for sales order confirmation in updatenow() method.
          • expandSurrogateForeignKeys(): This method return true by default, which means that if there is any foreign key exist on the table than the framework can automatically convert the input for that field into replacement key. For ex: If I am creating a customer and my customer has a foreign key relation with custGroup. Now if this method is set to return true than it will take custGroupId as input and at backend find the surrogate key and associate it with RecId of custgroup record and populate the recId on custTable.
          • PrepareForSaveExtended(): This method is the entry point and is called in sequence with the data sources mentioned on the query in AX2012 R3. This method is very important with the customization point of view. We can include the value default logics, validations and custom field value handling in this method.
          2. Terminology:  While going through the Axd classes, you can come across lot of terms which  are new, explaining few terms below which will help in understanding the code written in axdClasses.

          • AxdRecordProcessingContext: Record processing context is defined based on the data source levels defined on the document service query. Based this context Axd class proceed to create the records in child and parent tables. This term is basically found in prepareForSaveExtended method(). This method has been called multiple times when the record are processed in respective table. we can also define our custom code based on these contexts.
          • AxdRecordAction: This is another base enum which can be used for executing the particular logic based on the record action type. Record action type is defined by the operation which is triggered, basically there are create, read, update and delete operations. For ex. If wee need to perform some logic related to calculation of discount on a sales order if there is change in customer account. So, in order to update the customer account we will trigger the update operation. now we can place our code in prepareForSaveExtended() method below the relared processing context in following manner.
            if  (_axBcStack.top().recordAction() == AxdRecordAction::Update)
            {
              Perform your business logic and set parm value accordingly.

            }

          In my next post I will demonstrate the difference between service operations and their relevance.