Thursday, May 24, 2012

Services in AX 2012 - working example


In Microsoft Dynamics AX 2012, the Application Integration Framework (AIF) underwent a number of dramatic changes and is now known as Services. There are more number of WCF service hosts and simplified forms and concepts.
There are new terms like Service Groups and Ports (Basic, Enhanced), which we need to understand.
Service group - collection of Services. Why would you collect multiple services under a group? Consider a case of creation of a sales order and customer specified doesn't exist in AX. You will need another service for creation of customer. Both these services can be now clubbed together under one Service group. Also, with a single WSDL, you only need to add one service reference in Microsoft Visual Studio to access all of the objects from the services in the Service group.
Ports - Erstwhile (read AX 2009) local endpoints, channels and endpoints have been consolidated into a port.
Basic port - created automatically as soon as you have deployed your Service group. Also, Basic ports are deployed as WCF services only to the AOS and use the NetTcp adapter.
Enhanced port - created manually. They give you several options around hosting, adapter types, document processing, error handling and security. The choices of adapter types are:
•    File system adapter
•    HTTP- To consume services over the Internet, you must use the HTTP Adapter and host services on IIS. IIS routes all service requests to the AOS. Regardless of the origin of the service request, Internet or Intranet, all the service requests are processed on the AOS.
•    MSMQ - Similar to File adapter. For the URI of the Address and Response addresses browse to the directories that are mail queue directories.
•    NetTcp- Use this adapter when you want to use a service on the Intranet and not on the Internet.

Using the File Adapter
In this example, you will configure an enhanced port to use the file system adapter. Then you will import a sales order using the port.
Overview of the task
1. Create a new enhanced port.
2. Set the Adapter to File system adapter.
3. Set the default file owner to the administrator account.
4. Add the SalesOrder create operation to the list of service operations exposed on this port.
5. Activate the port.
6. Create an xml document that contains a sales order.
7. Run an X++ job that runs the AIF classes and imports the sales order.

Step by Step Instructions
Create and Activate an Enhanced Port
1. In Windows Explorer, create a Services directory and inside of it create an Inbound and an Outbound directory.
2. In Microsoft Dynamics AX under System administration, click Setup, click Services and Application Integration Framework, click Inbound ports and then click New.
3. Enter the name FileSystemAdapterPort for the port.
4. Click Register Adapters to ensure you see all of the adapters available in the environment.
5. Click Register services to ensure you see all of the service operations available in the environment.
6. In the Address section for the Adapter field, select File system adapter.
7. In the Address section for the URI field, click the dropdown and navigate to the Services - Inbound directory.
8. In the Response address section for the Adapter name, select File system adapter.
9. In the Response address section for the URI field, click the dropdown and navigate to the Services - Outbound directory.

10. In the Address section, click Configure.
11. Mark the Enable default file owner and select the Admin user. Admin will be the default owner of the xml files submitted to the Inbound directory.
12. Click Services operations.
13. From the Remaining service operations list, select SalesSalesOrderService.create and click the < arrow.
14. Click Close.
15. Click Activate.
16. Close the ports form. 
 
Create a XML File

1. In Windows, open Notepad.
2. Copy the following to a new Notepad document: (Note: Not able to display the below XML code in the blog, somehow displays junk characters, workaround i found is to use # in front of each row. Take care to remove the # in your actual code.)
# 
# 
#
#northamerica\dyndust #CEU #http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create #
#Body # # # #DAT-000001 #2011-01-16 #en-us #PO123 # #1001 #88.00 #ea # #HD #01 #2 #42 # # # # # #/Body #
 
3. Save the file in the Services - Inbound directory. Name the file SalesOrder.xml.

Create a Job

1. In the AOT, create a new job with the following code in it:

AifGatewayReceiveService agrs = new AifGatewayReceiveService(); 
AifInboundProcessingService aip = new AifInboundProcessingService(); 
AifOutboundProcessingService aop = new AifOutboundProcessingService(); 
AifGatewaySendService agss = new AifGatewaySendService(); 
; 
agrs.run();
aip.run(); 
aop.run(); 
agss.run();


2. Save the job.
3. Execute the job.

Verify the Results
 In AX, navigate to Sales and marketing, click Common, click Sales orders and then click All sales orders. You should have a new order created for the customer account 1101. If you do not have a new sales order in Microsoft Dynamics AX, navigate to System administration, click Services and Application Integration Framework and then click Exceptions to see the exceptions logged.

Wednesday, May 16, 2012

Model and model store cmdlets using Windows PowerShell

Quoting from TechNet
Windows PowerShell is a command-line scripting tool that lets administrators interact directly with Microsoft Dynamics AX to add users and user authentication, manage models, and manage configurations for communication between Microsoft Dynamics AX and Microsoft SQL Server Reporting Services. In addition, administrators can use Windows PowerShell cmdlets to create scripts. This term is pronounced "command-lets."

After you install Microsoft Dynamics AX, the applicable Windows PowerShell cmdlets are available from either the Microsoft Dynamics AX Management Shell or the Windows PowerShell console.

There is another a command-line utility called AXUtil, but this will be discontinued in a future version. So it's better we stick to PowerShell.

Access the Microsoft Dynamics AX 2012 Management Shell
1.    On the Start menu, click All Programs.
2.    Click Administrative Tools.
3.    Click Microsoft Dynamics AX 2012 Management Console.

Most common cmdlets can be found at the following TechNet link.
http://technet.microsoft.com/en-us/library/hh580552.aspx

Tuesday, May 8, 2012

Android time again, Version 1.2 for the app "Missed Calls" is launched! Now you can CALL and SMS from the app itself.
Next Version with more enhanced features is coming up soon. Do not forget to provide your inputs/ feedback for further improvement.

Wednesday, May 2, 2012

UnitOfWork - with example code

AX 2012 has a new concept of UnitOfWork aimed at managing database transactions. It's better than ttsbegin/ttscommit way of batching multiple transactions since that had some shortcomings like

•    transactions will start and commit on the client, UOW is on server.
•    transactions are open for a longer period
•    multiple RPC calls to the server, UOW takes a single call for multiple transactions
•    blocked in case there is user interaction inside tts scope

One interesting feature in UOW is that it automatically propagates the primary key value to the corresponding foreign key field, when the row with the foreign key field is inserted.

Methods at work
The UOW object takes a series of individual rows as parameters. It is designed to successfully process each row or it will reject all changes if any issue arises. In addition, the UOW class cannot support set based operations. It operates only on the specific rows that it is given.

Insert
insertOnSaveChanges()

Select
optimisticLock keyword on the select statement for retrieving the objects for updates/deletes

Update
updateOnSaveChanges()

Delete
deleteOnSaveChanges()

Persist to DB
saveChanges()

Example
We will create a parent/child tables example to understand UOW.

1. Create two tables.
TabSale has a primary key (RecId) foreign relation with TabLineItemOfSale.
Fields for the parent table TabSale:
• SaleName
• SaleComment

Fields for the child table TabLineItemOfSale:
• LiosName
• LiosComment
• MasterSaleRecIdFky – the foreign key

Create a foreign key relation on TabLineItemOfSale, name it TabSale, table property as TabSale, RelatedTableRole as masterSale and CreateNavigationPropertyMethods as Yes.
Note that the method masterSale is not a physical instance method on TabLineItemOfSale but is created run time to propagate the primary key value to the foreign key.


Below text is copied from MSDN link.
http://msdn.microsoft.com/en-us/library/hh803130.aspx
When you set the CreateNavigationPropertyMethods property to Yes on a table relation, the system generates navigation methods for the table buffer class. A navigation method links two table buffer instances by their foreign key relationship. The UnitOfWork class is one area where this navigation linkage is used.
The name for a navigation method is copied from the value of the RelatedTableRole property on the table relation. This is true when the RelatedTableRole value is set explicitly in the Properties window, and when the RelatedTableRole value is generated by setting the UseDefaultRoleNames to Yes.

2. Create a class UnitOfWorkEg with a server static method runUnitOfWorkEg and paste below code in that.
server static public void runUnitOfWorkEg()
{
    UnitOfWork uow = new UnitOfWork();
    TabSale tSale; // Buffer for parent table.
    TabLineItemOfSale tLineIos; // Buffer for child table.
    int64 i64MasterSaleRecIdFky;


    // Delete all rows, without using UoW.
    delete_from tLineIos;
    delete_from tSale;
    // Prepare a parent row for insert.
    // We let the system assign a RecId value, the primary key.
    tSale.SaleName = "Big";
    tSale.SaleComment = "A row in the parent table.";

    // Prepare a child row for insert.
    // Again, we let the system assign a RecId value, the primary key.
    // We also let the system assign SaleRecIdFky foreign key value!
    tLineIos.LiosName = "Chair";
    tLineIos.LiosComment = "To sit in.";
    // Method name is the RelatedTableRole property value.
    tLineIos.masterSale(tSale);
    // Prepare the UoW to do the inserts.

    uow.insertOnSaveChanges(tSale);
    uow.insertOnSaveChanges(tLineIos);
    // Before saving changes, prepare more inserts.
    // Add a second child to the current parent record.
    tLineIos.LiosName = "Desk";
    tLineIos.LiosComment = "To work at.";
    tLineIos.masterSale(tSale);
    uow.insertOnSaveChanges(tLineIos);
    // Add a second pair of parent + child records.
    tSale.SaleName = "Small";
    tSale.SaleComment = "Another row in the parent table.";
    tLineIos.LiosName = "Shirt";
    tLineIos.LiosComment = "To wear.";
    tLineIos.masterSale(tSale);
    uow.insertOnSaveChanges(tSale);
    uow.insertOnSaveChanges(tLineIos);
    // Make the changes to the SQL database, and commit.
    uow.saveChanges();

    //------------------------------------
    // Read the newly inserted child row.
    // Use optimistic concurrency, in case OccEnabled=No on the table.
    select optimisticLock
    LiosComment, MasterSaleRecIdFky
    from tLineIos
    where tLineIos.LiosName == "Desk";
    i64MasterSaleRecIdFky = tLineIos.MasterSaleRecIdFky;
    tLineIos.LiosComment = tLineIos.LiosComment + " Appended.";
    // Prepare the UoW to do the update. Then update.
    uow.updateonSaveChanges(tLineIos);
    uow.saveChanges();
    // All changes are complete. Display the results.
    tLineIos = null;
    tSale = null;

    select LiosName, LiosComment, RecId, MasterSaleRecIdFky
    from tLineIos
    where tLineIos.LiosName == "Desk";

    select SaleName, SaleComment, RecId
    from tSale
    where tSale.RecId == i64MasterSaleRecIdFky;
    // Display the parent RecId and the matching child foreign key.
    info(strFmt("TabSale: RecId=%1 , SaleName=%2",
    tSale.RecId, tSale.SaleName));
    info(strFmt("TabLineItemOfSale: MasterSaleRecIdFky=%1 , LiosName=%2 , RecId=%3 , LiosComment=%4",
    tLineIos.MasterSaleRecIdFky, tLineIos.LiosName, tLineIos.RecId, tLineIos.LiosComment));
}

3. Create a job UnitOfWorkExample with below code.
static void UnitOfWorkExample(Args _args)
{
    UnitOfWorkEg::runUnitOfWorkEg();
}

4. Run the job and see the output.