Dynamics NAV Generic SOAP Client

Last summer, when I posted my first article I though blogging is very easy, and I’ll post an article every week. Actually, it’s not that easy. Anyway, after few months here is the article, I promised: a 100% generic web service client for NAV.

I was looking for a way to automate all my web service calls without managing, deploying, and struggling with all Dlls generated by Visual Studio. I was hoping to use CU 1290 SOAP Web Service Request Mgt. like described here. I quickly noticed that it is not working if the web service comes from another NAV Database (due to SOAP version used natively by NAV). So, I started looking around how Visual Studio generates Dlls I was used to use. I found the answer in this wonderful article from msdn.

This introduction begins to be longer than expected. So, let’s get into the bare bones of the topic.

As you guessed, I tried to translate the msdn article using .NET interoperability in NAV. The result was astonishing. I let you admire the similarities:

Pure C#
Pure C#
Pure C/AL
Pure C/AL

The C/AL code remembers me this great article. My first reaction was: What the he**, I’m writing code like the great Vjekoslav Babić.

Now, let me explain how it works. The CU uses four functions:

1- InitClient: compiles the WSDL on the fly and prepares a ready to use client (proxy, dll… or whatever you want to call it) in memory.
2-InitCallMethod: prepares the method to call with its parameters.
3- CallMethod: invokes the method and retrieves the web service result.
4- GetObjectValue: use the web service result in whatever way you want (result may be simple Text,Decimal… or complex Customer Record…).

Here is an example:

How to use

and this is the result:

Example

Enough talking, you can download the code here gws-with-examples.

Some readers may say:

But this needs to compile the client every time; this is huge for the memory

This is true. The answer will be the subject of one of my next posts.

16 thoughts on “Dynamics NAV Generic SOAP Client”

  1. Dr. Zappergeck

    I’d love to try that, but I can’t import the fob into 2009 R2. It would be great if you could provide the objects in text format!

  2. Hi Dr. Zappergeck,

    The code is for NAV 2016 and later. I did not test it in old versions. It could work for NAV 2015, 2013 R2 and 2013. But I will be surprised if it works for NAV 2009 R2 RTC. For NAV 2009 R2 Classic Client it won’t work for sure.

    I can upload the text format and let you try to downgrade it ;).

    1. Dr. Zappergeck

      Thank you very much! As you expected, it will not work in the classic client, as .Net variables only work in RTC. But it will be usful for later versions.

  3. Thank you, a very interesting post, but I am getting an error :

    A call to System.Web.Services.Description.ServiceDescription.Read failed with this message: Root element is missing.

    This kind of error happens in C# when the XML file is not correct, but in our case, the XML file is generated by Nav is supposed to be correct, so why I am getting this error, anyone has an idea, please.

    Thank you

    1. Hello Abdelatif,

      When you say it “is generated by NAV and it is supposed to be correct”, you’re using an XMLport? XMLports usually add an XML tag to your file structure. Maybe you should read your XML file in a string and check what is wrong with it?

    2. Hi, i try the described things. i obtain too the Error Message “A call to System.Web.Services.Description.ServiceDescription.Read failed with this message: Root element is missing”. I Publish Codeunit SayHelloWS in NAV2009, and in NAV2018, and then in the TestGenericSoapClient i try consume the webservice from 2009 or 2018 , in both i obtain the error.

      InitClient(‘http://nav2009server:7047/DynamicsNAV/WS/Codeunit/SayHelloWS’, 0, ‘webserviceuser’, ‘pass’, 2);

      InitCallMethod(‘SayHello’, Arguments);
      // Set arguments if needed

      CallMethod(Arguments, ReturnObject);
      MESSAGE(‘Example 1:\ %1’, GetObjectValue(”, ReturnObject));

  4. Raik Zobel

    Thank you so much!!! I’m searching such a solution for so long.
    I had to to some slighly modification with authentification, because it will otherwise not work when you want to communicate between different domains.

    @TryGenerateCSharpCode

    //replace:
    webRequest.UseDefaultCredentials := TRUE;

    //with:
    IF _AuthenticationType = _AuthenticationType::”User/Password” THEN BEGIN
    NetCredential := NetCredential.NetworkCredential(_UserName,_Password);
    webRequest.Credentials := NetCredential;
    END ELSE
    webRequest.UseDefaultCredentials := TRUE;

    //Name DataType Subtype Length
    //NetCredential DotNet System.Net.NetworkCredential.’System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′

    And this for the correct error return if something goes wrong at GetWebResponse:

    //change:
    IF NOT WebRequestHelper.GetWebResponse(webRequest, webResponse, NavInstream, HttpStatusCode, ResponseHeaders, FALSE) THEN
    WebRequestHelper.GetWebResponseError(WebException, ServiceURL);

    //to:
    IF NOT WebRequestHelper.GetWebResponse(webRequest, webResponse, NavInstream, HttpStatusCode, ResponseHeaders, FALSE) THEN BEGIN
    WebRequestHelper.GetWebResponseError(WebException, ServiceURL);
    ERROR(GETLASTERRORTEXT);
    END;

    @TryGenerateCSharpCode

    //replace:
    webRequest.UseDefaultCredentials := TRUE;

    //with:
    IF _AuthenticationType = _AuthenticationType::”User/Password” THEN BEGIN
    NetCredential := NetCredential.NetworkCredential(_UserName,_Password);
    webRequest.Credentials := NetCredential;
    END ELSE
    webRequest.UseDefaultCredentials := TRUE;

    //Name DataType Subtype Length
    //NetCredential DotNet System.Net.NetworkCredential.’System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′

    And this for the correct error return if something goes wrong at GetWebResponse:

    //change:
    IF NOT WebRequestHelper.GetWebResponse(webRequest, webResponse, NavInstream, HttpStatusCode, ResponseHeaders, FALSE) THEN
    WebRequestHelper.GetWebResponseError(WebException, ServiceURL);

    //to:
    IF NOT WebRequestHelper.GetWebResponse(webRequest, webResponse, NavInstream, HttpStatusCode, ResponseHeaders, FALSE) THEN BEGIN
    WebRequestHelper.GetWebResponseError(WebException, ServiceURL);
    ERROR(GETLASTERRORTEXT);
    END;

      1. Raik Zobel

        Ah.. thank you. I will look at the objects at the open library.
        I see that my answer contains duplicate text. sorry for that.

        1. Yeah I noticed the duplicate text, no worries
          Please share the link if you find the codeunit. I’ll update the post with it later. If not, I’m sure I have a copy somewhere, but I must find it too !

        1. No, not the codeunit 1290. There is (was) another one in the repository!
          I didn’t check my archives yet. But I’m sure I have it somewhere

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top