NAV 2009 R2 has been released for about two weeks now, so it's time to get you up-to-speed on some topics. i'm sure lots of blogs are picking this up, so exciting times are coming :-).
What I have been struggling with lately (and I mean that literally...) is being able to use .Net Interop for something useful .. calling a Web Service. You probably all have seen this in a video that was broadcasted a while ago. I didn't, and that was also the reason why I was struggling, probably ;°). Here is the video: http://www.mibuso.com/dlinfo.asp?FileID=1272. I strongly recommend you to watch it. Some very simple examples and explanation on how to use .NET Interop.
I am a C/AL developer, not a .Net developer. People might think otherwise because of my little project: WaldoNavPad, but no .. with working on this blog, I was reminded in the wealth I was developing (called C/AL, Dynamics NAV, C/SIDE, ...). It's difficult to make the ".NET-switch", and I'm afraid I'm not going to be the only one.. . therefore I'm even more motivated to put up a few blog posts about this great new feature we have in C/AL.. .
What are we gonna do ...
We're going to call a Web Service. You know that publishing a web service has been a peace of cake. Freddy has shown this is in lots of blog posts .. I did a few myself. But what about consuming a Web Service in NAV? The other way around. That hasn't been that easy (just read this article on Freddy's blog, or this article on my blog). Lots of lines of code, nothing straight forward ... .
How is NAV 2009 R2 .NET Interop going to help with this?
Well, we should be able to consume a web service like we do in Visual Studio. On MSDN, there is a walkthrough that helps you with that: http://msdn.microsoft.com/en-us/library/gg502468.aspx. First thing I did was trying to rebuild this scenario. I didn't succeed very well, I must say [:-(]. Probably it was just me, but a few things were left unmentioned, in my opinion.. . I was planning for another scenario, but because of the fact that I had so many troubles in doing the walkthrough, I decided to explain it a bit further so that you ARE able to do this :-). I just changed one thing: I want to read customers, not sales orders.. .
It's a good example on how to connect two NAV databases, companies, ... and let them talk to each other.
STEP 0: Scenario
I published page 22 as a web service in my Dynamics NAV 2009 R2 Database. I've got 2 companies: "First Company" and "Second Company". I want to be able to read customers from company "First Company" in company "Second Company"(sorry for the naming... :-)).
STEP 1: Publish the Web Service
Publishing a page web service is a piece of cake. You all know this. Just add a record in the form (or page) "Web Services":
In my case, this URL represents the web service of company "Second Company": http://localhost:7047/DynamicsNAV/WS/Second Company/Page/Customers
STEP 2: Creating a proxy class
Don't think this step is added to make it difficult on you. On the contrary. We have to create a proxy class to make it easy on you. In stead of exchanging XML, a proxy class makes it possible to use classes to communicate with your web service. You can find more information about a proxy class on the web (Bing it..)
I don't recommend to use the wsdl.exe-tool to create a web service. I did it, and I ended up with a proxy that wasn't useful at all. Therefore, try to follow these steps:
STEP 2: Deploy to Development environment
STEP 3: Write your code
In this step, you basically do what was described in the walkthrough. First of all, you declare your variables:
'System.ServiceModel, Version=220.127.116.11, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.ServiceModel.BasicHttpBinding
'Customer, Version=18.104.22.168, Culture=neutral, PublicKeyToken=null'.Customer.CustomerService.Customers
'System.ServiceModel, Version=22.214.171.124, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.ServiceModel.EndpointAddress
'Customer, Version=126.96.36.199, Culture=neutral, PublicKeyToken=null'.Customer.CustomerService.Customers_PortClient
As you can see, we're using the new datatype "DotNet". I wrote (and copied) thise simple lines of code to read customer 10000:
navBinding := navBinding.BasicHttpBinding;// Set security mode to BasicHttpSecurityMode.TransportCredentialOnlynavBinding.Security.Mode := 4; address := 'http://localhost:7047/DynamicsNAV/WS/Second Company/Page/Customers';// Set client credential type to HttpClientCredentialType.WindowsnavBinding.Security.Transport.ClientCredentialType := 4;CustomerService := CustomerService.Customers_PortClient(navBinding, endpointAddress.EndpointAddress(address));// Set impersonation level to System.Security.Principal.TokenImpersonationLevel.DelegationCustomerService.ClientCredentials.Windows.AllowedImpersonationLevel := 4;// Include the sales order ID to be read.Customer := CustomerService.Read('10000');MESSAGE(Customer.Name);
Probably for the .NET-ers amongst you, this is peace of cake. I'm not one of those, so for me, it was a little bit more difficult to write and understand the code .. Thank you Microsoft for the example :-).
STEP 4: Deploy on RTC Environment
To be able to use this, the dll has to be available at the instance where the business logic is run. Therefore, you have to copy it it to the service tier "Add-Ins" folder, which is located in this map (or similar): C:\Program Files\Microsoft Dynamics NAV\60\Service\Add-ins
Copy the dll to that map as well...
STEP 5: Test your code in the RTC
It's important to know, to be able to run .NET interop code, that you have to be in a client that is using the .NET Framework. Obviously, the RTC is using it and the Classic is not. Therefore, it's not possible to run your .NET Interop Code in the Classic client. You have to find a way to run it in the RTC.
What I did: I added an action on page 9006 (the default Role Center of my testing environment) and just ran the codeunit... .
But what if I wanted to loop my data??
No prob .. . It's actually quite well explained in this blog from Stuart. When calling the "ReadMultiple" method, the web service returns an Array (of the type System.Array). You're able to loop this Array. To test this, I added two more variables to my list:
'mscorlib, Version=188.8.131.52, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array
'Customer, Version=184.108.40.206, Culture=neutral, PublicKeyToken=null'.Customer.CustomerService.Customers_Filter
Notice you'll have to go to Assembly "mscorlib" to get to the System.Array namespace (sometimes, it's a real struggle to find what you're looking for... ).
Furthermore, I added this to my code:
MyCustomerList := CustomerService.ReadMultiple(CustomerFilter,'',100);FOR i := 0 TO MyCustomerList.Length() -1 DO BEGIN Customer := MyCustomerList.GetValue(i); MESSAGE('Looping Customers: %1 %2', Customer.No, Customer.Name);END;
That's all to it .. simple, isn't it? One small remark: if you want to have lots of records returned, you might have to add an extra line of code to set the Maximum message size. It's the navbinding.maxReceivedMessageSize which I set to 100000 to be able to have all my customers returned... .
When using web services, you're quite forced to go into Visual Studio for a few minutes to create you the proxyclass that you can use ... In fact .. You have to extend you .NET Framework with some webservice-translation-stuff... . Done that, don't forget to deploy: as well as to the classic (to be able to work with it in the dev environment) as to the RTC (server, of course, to be able to use it).
But once you get through these steps, it's REALLY easy to use your web service and go from there.. .
Another thing you should keep in mind .. It should always be executed by the Service Tier! So, remember this blog? where I consumed a NAV 2009 Web Service to print a RTC report in the classic environment? Code could look LOTS simpler in C/SIDE with .NET interop ... BUT ... it would be executed in C/SIDE, so that scenario would not work with .NET Interop ... .
Problem solved ... I have changed the client authentication to NTLM and it works. :) Thanks a lot for your help!