Web Services Infrastructure and how to Create an Internal Proxy

I am NOT an expert in how to setup a secure network and I do NOT know a lot about firewalls, DMZ setup and all of these things, but I have seen a lot in my 25 years of working with computers and the following (absolutely non-exhaustive) gives a good picture of a common network situation of companies, who wants to interact with customers and partners through Web Applications and/or Web Services. Continue reading

Connecting to NAV Web Services from Windows Mobile 6.5

It is kind of embarrassing that I write a post about how to connect to NAV Web Services from Windows Mobile 6, when Windows Mobile 6.5 has been out for almost half a year (that’s how much a gadget person I am:-))

I just downloaded the 6.5 SDK from here and tried out the exact same application as I wrote for Windows Mobile 6 in this post and everything seems to work just fine, so please follow the steps in this post to see how to connect from Windows Mobile 6.5 to NAV Web Services.

image_2 (2)

Is it faster?

My tests on 6.5 is only on the emulator – but as far as I can see, it is definitely faster. The 1.5 second from Windows Mobile 6 is now down to 0.9 second and 0.8 second with PreAuthenticate set to true.

I will still do some more investigations on performance from Windows Mobile Web Services.

Enjoy

Freddy Kristiansen
PM Architect
Microsoft Dynamics NAV

Connecting to NAV Web Services from Windows Mobile 6

I have created my very first Windows Mobile App!

image_2 (3)

This is running in an Emulator using the Professional SDK.

I also tried to deploy the solution to my physical device (my Smartphone), which also worked:

Smartphone_2

To be honest, the biggest challenge is to setup everything so that you can get going.

A couple of useful links to get going

Location to download Windows Mobile 6 SDK: http://www.microsoft.com/downloads/details.aspx?FamilyID=06111a3a-a651-4745-88ef-3d48091a390b&DisplayLang=en

The Windows Mobile 6 SDK is not included in Visual Studio 2008, you will have to download and install it.

 

Create your first WM6 App: http://code.msdn.microsoft.com/WM6YourFirstApp

A good video to help you get started.

 

Windows Mobile Development Center: http://msdn.microsoft.com/en-us/windowsmobile/default.aspx

This contains a lot of good links for getting started and how to do things.

 

Security Configuration Manager: C:\Program Files\Windows Mobile 6 \SDK\Tools\Security\Security Powertoy

When you have installed the SDK – go to this location and install the security configuration manager to be able to setup your device so that you can deploy your solution and debug it.

 

Note: I did struggle quite a bit to get network access up running on the device and on the emulator, but once I got the emulator setup to have network access (connected to Internet – not Work) and I had access through the firewall to my host machine – then everything worked fine.

The scenario

Please read this post to get a brief explanation of the scenario I will implement on a Windows Mobile Device.

.net 3.5

We will use 3.5 of the compact .net framework to build our application and whether you select Professional (first picture) or Standard (second picture) really doesn’t matter. First thing I do is to create two Web References from my app to the two Web Services i use in my scenario – SystemService (SystemServiceRef) and Customer Page (CustomerPageRef).

These Web References are pretty similar to .net 2.0 Web References from the normal .net framework (look this post). One thing to note is, that you do not have UseDefaultCredentials in the compact framework so you need to specify user and password when connecting to NAV Web Services.

The project type is a Device Application and the code on the form is:

using System; 
using System.Windows.Forms; 
using SmartDeviceProject4.SystemServiceRef; 
using SmartDeviceProject4.CustomerPageRef; 
using System.Net;

namespace SmartDeviceProject4 
{ 
    public partial class Form1 : Form 
    { 
        public Form1() 
        { 
            InitializeComponent();

            string baseURL = ":7047/DynamicsNAV/WS/";'>http://<IP>:7047/DynamicsNAV/WS/"; 
            NetworkCredential credentials = new NetworkCredential(user, password, domain);

            SystemService systemService = new SystemService(); 
            systemService.Credentials = credentials; 
            systemService.Url = baseURL + "SystemService"; 
            systemService.PreAuthenticate = true;

            display("Companies:"); 
            string[] companies = systemService.Companies(); 
            foreach (string company in companies) 
                display(company); 
            string cur = companies[0];

            string customerPageURL = baseURL + Uri.EscapeDataString(cur) + "/Page/Customer"; 
            display(""); 
            display("URL of Customer Page:"); 
            display(customerPageURL);

            Customer_Service customerService = new Customer_Service(); 
            customerService.Credentials = credentials; 
            customerService.Url = customerPageURL; 
            customerService.PreAuthenticate = true;

            Customer customer10000 = customerService.Read("10000"); 
            display(""); 
            display("Name of customer 10000:"); 
            display(customer10000.Name);

            Customer_Filter filter1 = new Customer_Filter(); 
            filter1.Field = Customer_Fields.Country_Region_Code; 
            filter1.Criteria = "GB";

            Customer_Filter filter2 = new Customer_Filter(); 
            filter2.Field = Customer_Fields.Location_Code; 
            filter2.Criteria = "RED|BLUE";

            display(""); 
            display("Customers in GB served by RED or BLUE warehouse:"); 
            Customer_Filter[] filters = new Customer_Filter[] { filter1, filter2 }; 
            Customer[] customers = customerService.ReadMultiple(filters, null, 0); 
            foreach (Customer customer in customers) 
                display(customer.Name);

            display(""); 
            display("THE END"); 
        }

        private void display(string s) 
        { 
            this.textBox1.Text += s + "\r\n"; 
        } 
    } 
}

As you can see 99% of the code is similar to the post about C# and Web References (found here). Major differences are that the baseURL of course isn’t localhost (since localhost would be the mobile device itself) and I have to setup credentials in the beginning.

But… It is Very Slow!

Having done this and finally have everything working, you will probably find that Web Services from a mobile device is extremely slow.

Call a service to get Customer 10000 takes approx. 1.5 second – and it is not getting faster if you do it 100 times.

If you set service.preAuthenticate to true – then the time is down to 1.2 second, but still – slower than I would like.

I tried to create a standard .net Web Service on my host computer (asmx web service – just the Hello World sample) and tried to call this method 100 times and in this case, the time was down to around 0.5 second pr. call – still very slow, but more acceptable.

When running some of the other applications a call to a webservice (including authorization) is only around 0.04 seconds on my computer so we are looking at around 30 times slower pr. web service call from a mobile device.

I also tried to make my Hello World application return a 10k string – this didn’t affect the performance at all – and when upping the size of the string to 40k – the time climbed to around 0.7 second pr. call – it seems like the biggest problem is latency (only guessing).

I will do some more investigation on this – including contacting the mobile team in Microsoft to figure out why and how to fix this (if possible).

For now the solution seems to be to create some proxy (with a very limited instruction set = one method for each high level thing the mobile device is capable of doing) running with no authentication and then have the mobile devices communicate with that – maybe using some kind of poor mans authentication – or simply having IP security on the Web Service.

I hope this is helpful.

Good luck

Freddy Kristiansen
PM Architect
Microsoft Dynamics NAV

Connecting to NAV Web Services from Visual Basic .net using Web Reference

This post is really just a Visual Basic version of this post, please read that post before continuing.

Note, this is my very first Visual Basic application. I don’t think there are any ways to do this easier – but then again – how should I know.

I am creating a Visual Basic Console application and adding two Web References (like it is done in this post) and then it is really just writing the code

The code

Module Module1

    Sub Main()

First, connect to the System Web Service and list all companies:

        Dim baseURL As String = “http://localhost:7047/DynamicsNAV/WS/”

        Dim systemService As New SystemServiceRef.SystemService()
systemService.Url = baseURL + “SystemService”
systemService.UseDefaultCredentials = True
Dim companies() As String = systemService.Companies()
Console.WriteLine(“Companies:”)
For Each company As String In companies
Console.WriteLine(company)
Next
Dim cur As String = companies(0)

Now I have the company I want to use in cur and the way I create a URL to the Customer page is by doing:

        Dim customerPageURL As String = baseURL + Uri.EscapeDataString(cur) + “/Page/Customer”
Console.WriteLine(vbCrLf + “URL of Customer Page: ” + customerPageURL)

and then I can create a Service Class to the Customer Page:

        Dim customerService As New CustomerPageRef.Customer_Service()
customerService.Url = customerPageURL
customerService.UseDefaultCredentials = True

and using this, I read customer 10000 and output the name:

        Dim customer10000 As CustomerPageRef.Customer = customerService.Read(“10000”)
Console.WriteLine(vbCrLf + “Name of Customer 10000: ” + customer10000.Name)

Last, but not least – lets create a filter and read all customers in GB that has Location Code set to RED or BLUE:

        Dim filter1 As New CustomerPageRef.Customer_Filter()
filter1.Field = CustomerPageRef.Customer_Fields.Country_Region_Code
filter1.Criteria = “GB”

        Dim filter2 As New CustomerPageRef.Customer_Filter()
filter2.Field = CustomerPageRef.Customer_Fields.Location_Code
filter2.Criteria = “RED|BLUE”

        Console.WriteLine(vbCrLf + “Customers in GB served by RED or BLUE warehouse:”)
Dim filters() As CustomerPageRef.Customer_Filter = New CustomerPageRef.Customer_Filter(1) {filter1, filter2}
Dim customers() As CustomerPageRef.Customer = customerService.ReadMultiple(filters, Nothing, 0)
For Each customer As CustomerPageRef.Customer In customers
Console.WriteLine(customer.Name)
Next

        Console.WriteLine(vbCrLf + “THE END”)
Console.ReadLine()
End Sub

End Module

I hope this is helpful.

Good luck

Freddy Kristiansen
PM Architect
Microsoft Dynamics NAV

Connecting to NAV Web Services from C# using Web Reference

Prerequisites

Please read this post to get a brief explanation of the scenario I will implement in C# using Web References.

For C# we can leave the Service Tier running Negotiate or we can use Ntlm as PHP and Java. In this example I will assume that the Service Tier is running SPNEGO (which is the default)

BTW. Basic knowledge about C# is required to understand the following post:-)

Version and download

I am using Visual Studio 2008 professional with SP1 when writing this sample, to be honest I have NOT tried to see whether this will work in the Express versions of Visual Studio, but they do have Service Reference and Web Reference so I cannot see why not.

What is the difference between a Web Reference and a Service Reference?

In short, the Web Reference is a .net 2.0 compatible Web Service reference, the Service Reference is a .net 3.5 WCF based Service Reference.

Add Web Reference is a wrapper over wsdl.exe and can be used to create proxies for .NET 1.1 or 2.0 clients. Of course this means when you are pointing to a WCF service you have to be pointing to an endpoint that uses basicHttpBinding.

Add Service Reference is a wrapper over svcutil.exe and also creates clients proxies. These proxies, however, can only be consumed by .NET 3.5 clients.

How to add Web References

Select Add Service Reference

image

Click Advanced

image

Click Add Web Reference

image

Type in the URL and specify the namespace for the SystemService Web Reference

image

and for the Customer Page Web Reference

image

When adding a Web Reference, Visual Studio will create a config file in which it stores stuff like the URL for the Reference. In my samples I will set the URL in code and due to this, the config file is not needed.

Authentication

In the following sample I use Windows Authentication. In Web References you just need to set the property UseDefaultCredentials in the service class to true, then .net will automatically try to use your windows credentials to connect to the Web Service.

If you want to connect to a Web Reference using a specific username/password you need to exchange this line:

someService.UseDefaultCredentials = true;

with this:

someService.Credentials = new NetworkCredential(“user”, “password”, “domain”);

The code

First a couple of using statements (including the two reference namespaces) and the main body of a console app:

using System;
using System.Net;
using testApp.CustomerPageRef;
using testApp.SystemServiceRef;

namespace testApp
{
class Program
{
static void Main(string[] args)
{
// main program code
        }
    }
}

The main code follows

First, connect to the System Web Service and list all companies:

string baseURL = “http://localhost:7047/DynamicsNAV/WS/”;

SystemService systemService = new SystemService();
systemService.Url = baseURL + “SystemService”;
systemService.UseDefaultCredentials = true;

Console.WriteLine(“Companies:”);
string[] companies = systemService.Companies();
foreach (string company in companies)
Console.WriteLine(company);
string cur = companies[0];

Now I have the company I want to use in cur and the way I create a URL to the Customer page is by doing:

string customerPageURL = baseURL + Uri.EscapeDataString(cur) + “/Page/Customer”;
Console.WriteLine(“nURL of Customer Page: ” + customerPageURL);

and then I can create a Service Class to the Customer Page:

Customer_Service customerService = new Customer_Service();
customerService.Url = customerPageURL;
customerService.UseDefaultCredentials = true;

and using this, I read customer 10000 and output the name:

Customer cust10000 = customerService.Read(“10000”);
Console.WriteLine(“nName of Customer 10000: ” + cust10000.Name);

Last, but not least – lets create a filter and read all customers in GB that has Location Code set to RED or BLUE:

Customer_Filter filter1 = new Customer_Filter();
filter1.Field = Customer_Fields.Country_Region_Code;
filter1.Criteria = “GB”;

Customer_Filter filter2 = new Customer_Filter();
filter2.Field = Customer_Fields.Location_Code;
filter2.Criteria = “RED|BLUE”;

Console.WriteLine(“nCustomers in GB served by RED or BLUE warehouse:”);
Customer_Filter[] filters = new Customer_Filter[] { filter1, filter2 };
Customer[] customers = customerService.ReadMultiple(filters, null, 0);
foreach (Customer customer in customers)
Console.WriteLine(customer.Name);

Console.WriteLine(“nTHE END”);
Console.ReadLine();

All of the above will output the following to a console prompt (on my machine running NAV 2009SP1 W1)

image

I hope this is helpful.

Good luck

Freddy Kristiansen
PM Architect
Microsoft Dynamics NAV