Sunday, October 27, 2013

Consuming a Web Service in AX 2012 is easy

So there might be a little threshold before you know how to consume your first Web Service, but I encourage any AX developer to set aside at least half an hour to just play with this. There are a lot of good content out there describing how to consume Web Services in AX 2012 (TechNet, White Paper, Mukesh Blog, Joris Blog) so instead of repeating all of those good reads, I wanted to equip you with a dummy service of your own. Then it will be easy to consume it and you will have complete control over both the service providing data and the code in AX consuming the data.

I will assume you have the following installed:

  • Internet Information Server
  • Visual Studio 2010
  • Dynamics AX 2012 Client
  • Dynamics AX 2012 Visual Studio Tools
First, let us create a new Project in Visual Studio. I'm want something easy and swift, so let's choose .Net 3.5 and ASP.Net Web Service Application. I'm naming the Project "PizzaSearchService" and this will automatically be part of the namespace for this application. 


The project loads and you will be presented with the code for "Service1". Now just copy the code underneath and paste it in.

using System.ComponentModel;
using System.Collections.Generic;
using System.Web.Services;

namespace PizzaSearchService
{
    public class PizzaInfo
    {
        public string Name;
        public string Description;
        public double Prize;
    }

    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    public class Service1 : WebService
    {
        [WebMethod]
        public List< PizzaInfo > SearchPizza(string query)
        {
            return new List< PizzaInfo > { 
                new PizzaInfo{ 
                    Name = "AX Beef Extreme", 
                    Description = "One of our favourites with mushroom, beef, onion and mozzarella cheese.", 
                    Prize = 12.99 
                }, 
                new PizzaInfo{ 
                    Name = "AX Regular Meatlover", 
                    Description = "The good old classic with mushroom, meat, pepperoni, peppers and mozzarella cheese.", 
                    Prize = 10.99 } 
            };
        }
    }
}

This is just a very simple service that takes a string as an input for a query for pizzas. It then returns a list of two pizzas. I love pizza, so I just couldn't help myself. 

Now we need to build this and deploy it to Internet Information Server (IIS). Depending on your configuration of IIS you might need to prepare a site that runs on an Application Pool that uses .Net 2 instead of .Net 4. Let us quickly go through these steps. I am assuming you're not messing up the settings for some already existing Web Site. If that is the case, you might need to create a different Web Site where you can host this Web Service.

Open Internet Information Services Manager, right-click the Default Web Site and choose Manage Website and Advanced Settings...


Then click choose to select the correct Application Pool. By default IIS will have preconfigured some Applications Pools, and the one we want for now is the one named "Classic .Net AppPool", because it runs .Net 2, and our Web Service is of .Net 3.5 (built on .Net 2).


Having this set, you can head back to Visual Studio and Publish your built solution. Right-click the project and choose Publish...


Select "File System" as Publish method, and then choose a target Location.


Select Local IIS and your Default Web Site.


Now simply press Publish and your Service1.asmx and precompiled binaries will be copied to the location of your Web Site, normally under C:\inetpub\wwwroot\.


You should be able to test the Web Service by opening a browser and navigating to it. Try loading http://localhost/service1.asmx and see what happens. Unless something went horribly wrong, you should see this page listing service entry points and some extra textual description.


If you click the SearchService-link you will get a description of that service and since it takes a simple string you can invoke the service from here.


We already know the service returns the same result each time, so just press invoke and watch it open the result.



This only took you like 5-10 minutes and you're ready to consume this Web Service from within AX 2012. I recommend having a look at one of the blog posts linked above. In short, you need to do the following:

  • Create a new Visual Studio Project
    • Select .Net Framework 4
    • Select a template from Visual C# and Windows
    • Select the Class Library as template.
    • Give it a name like "DynamicsAXPizzaService".
  • Add a Service Reference and point to http://localhost/service1.asmx
  • Add the project to the AOT
  • Deploy!!
Now you are ready to consume it from within AX. You will have to restart the AX client, as already mentioned in the documentation. 

In order to get you started quickly, I wrote this main-method which you can just copy and paste to test if it works. 

public static void main(Args args)
{
    DynamicsAXPizzaService.WebService1.Service1SoapClient   wcfClient;
    DynamicsAXPizzaService.WebService1.PizzaInfo[]          pizzaInfoArray;
    DynamicsAXPizzaService.WebService1.PizzaInfo            pizzaInfo;

    System.ServiceModel.Description.ServiceEndpoint         endPoint;
    System.ServiceModel.EndpointAddress                     endPointAddress;
    System.Exception                                        ex;
    System.Type                                             type;
    int                                                     i, numOfPizzas;

    str name, description, prize;
    ;

    try
    {
        type            = CLRInterop::getType('DynamicsAXPizzaService.WebService1.Service1SoapClient');
        wcfClient       = AifUtil::createServiceClient(type);

        endPointAddress = new System.ServiceModel.EndpointAddress("http://localhost/service1.asmx");
        endPoint        = wcfClient.get_Endpoint();
        endPoint.set_Address(endPointAddress);

        pizzaInfoArray  = wcfClient.SearchPizza("mozarella");
        numOfPizzas     = pizzaInfoArray.get_Count();

        for(i = 0; i < numOfPizzas; i++)
        {
            pizzaInfo   = pizzaInfoArray.get_Item(i);
            name        = pizzaInfo.get_Name();
            description = pizzaInfo.get_Description();
            prize       = pizzaInfo.get_Prize();

            info(strFmt("%1 - %2 - %3", name, description, prize));
        }
    }
    catch(Exception::CLRError)
    {
        ex = CLRInterop::getLastException();

        while(ex)
        {
            info(CLRInterop::getAnyTypeForObject(ex.ToString()));
            ex = ex.get_InnerException();
        }
    }
}

The output when running this class should be this:


Now that you have this working, you can start tamper with it and make it break and learn how the pieces fits together. Here are a couple of things you might want to try understand:
  • What dll is being used when the X++ code is running client side?
    • Tip: have a look at this path: "%localappdata%\Local\Microsoft\Dynamics AX\VSAssemblies\"
  • What dll is being used when the X++ code is running server side?
    • Tip: find the location where your AOS Server files are installed and look for the VSAssemblies-folder under the bin-folder.
    • What about when you activate hot-swapping of assemblies on the AOS?
  • What happens if you deploy new versions of these dlls and you want the client or the AOS to load this new version?
    • Either restart the client or restart the AOS, depending on what dll you want reloaded.
  • What if you plan to have the dll run only server side and never client side, but you need intellisense while developing the X++ code?
    • You need the dll deployed client side on the developer machine. :-)
Finally, I wanted to show you a neat little tool by JetBrains named dotPeek. If you take any of the dlls you just created and drop them into this tool, you can explore the content and even browse the code. I have used this tool in many different scenarios to peek inside managed assemblies.


If you have any concerns or you bump into any issues while trying to follow the steps in this article, please leave a comment underneath.