Don’t worry about DotNet version in C/AL

When using DotNet data type in NAV C/AL we normally lookup a sub type to use.  When we do the result can be something like

Then, what will happen when moving this code from NAV 2016 to NAV 2017 and NAV 2018.  The Newtonsoft.Json version is not the same and we will get a compile error!

Just remove the version information from the sub type information.

And NAV will find the matching Newtonsoft.Json library you have installed and use it.

This should work for all our DotNet variables.

REST Web Services using Json and requiring authentication

But first…

Registration for NAV TechDays 2017 have been opened.  I will do a workshop on web services and json.  I will be using both C/AL and AL with VS Code in this workshop.

Make sure to register for the conference and if possible go to one or two of the workshops.

Now to the topic.  Yesterday I started to develop an integration solution for bokun.io.  Their API is RESTful and uses Json file formats.  It also requires authentication.

In a project like this I usually start by using the OCR Service Setup from standard NAV.  Create a Setup table and a page.

Looking at the API documentation we can see that we need to use HmacSHA1 with both Access Key and Secret Key to authenticate.  In other project I used HmacSHA256 with the Access Key for the Azure API.

First part of the authentication is the time stamp created in UTC.  I find it easy to use the DateTime DotNet variable to solve this.  There are two different formatting I needed to use.

REST service normally just use GET or POST http methods.  The authentication is usually in the request headers.  This is an example from bokun.is

The GetSignature function is

The Secret Key string and the Signature is converted to a byte array.  The Crypto class is constructed with the Secret Key Byte Array and used to compute hash for the Signature Byte Array. That hash is also a byte array that must be converted to a base64 string.  This will give you the HmacSHA1 signature to use in the request header.

My Azure project is using HmacSHA256 but the code is similar.

Azure displays the Access Keys in base64 format while bokun.is has a normal string.

A little further down the line I choose not to use XML Ports, like I did here, but still convert Json to Xml or Xml to Json.

I use the functions from Codeunit “XML DOM Management” to handle the Xml.  This code should give you the general idea.

 

 

Mr. Singelton I presume

We do have one Mr. Singelton in our Dynamics NAV MVP group.  However, this blog post is not about him.

Singelton is a well known Design Pattern.  A Singelton object is a one instance object that is shared in the whole application.  Vjeko showed an example of this in his recent post.

With a Singelton class it is possible to store values that can later be retrieved by another call not related to the first.

It is common practise in Dynamics NAV to create a single-instance Codeunit to keep data that needs to be accessible from all processes within the user session.  A Singelton class is common for all user sessions on the server.

The example Vjeko talks about it that you can store data in a Singelton class to implement a state information into an otherwise stateless web service.

In a recent post I talked about a Variable Store Codeunit.  That was a single-instance Codeunit where I used the DotNet Dictionary to store and retrieve values.  This worked well inside a single user session.

I wanted to make a similar functionalilty available as a Singelton class that would be common for all sessions.  The class code is in c#

[code lang=”csharp”]using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SingeltonDictionary
{
public class SingeltonDictionary
{
private static SingeltonDictionary instance;
private Dictionary<string, string=""> NavDictionary = new Dictionary<string, string="">();

private SingeltonDictionary() { }

public static SingeltonDictionary Instance
{
get
{
if (instance == null)
{
instance = new SingeltonDictionary();
}
return instance;
}
}
public bool AddToDictionary(string KeyName, string Value)
{
if (NavDictionary.ContainsKey(KeyName))
return false;
else
{
NavDictionary.Add(KeyName, Value);
return true;
}
}
public void AddReplaceInDictionary(string KeyName, string Value)
{
ClearFromDictionary(KeyName);
NavDictionary.Add(KeyName, Value);
}
public bool ClearFromDictionary(string KeyName)
{
if (NavDictionary.ContainsKey(KeyName))
{
NavDictionary.Remove(KeyName);
return true;
}
else
return false;
}
public bool GetFromDictionary(string KeyName, ref string Value)
{
return NavDictionary.TryGetValue(KeyName,out Value);
}
public void ClearDictionary()
{
NavDictionary.Clear();
}
}
}
[/code]

The Singelton Dictionary NAV add-in can be downloaded here -> Microsoft.Dynamics.Nav.SingeltonDictionary

There is another reason why I wanted to create this add-in.  I have a solution for the Windows Client to be able to easily create textboxes and buttons for a NAV Page.  One of the problems with this add-in can be solved with a Singelton class.

The solution has a Panel Class that is built when the add-in is initiated on the page.  On top of that panel any number of buttons and textboxes are added dynamically.  To support this the panel has the autosize property set and will resize as needed.

The problem is that you can’t start to add things to the panel until it has been initiated and at that time NAV draws the page and decides how much space is needed for the panel.  At the time NAV decides the panel is empty and therefore no space is allocated for the panel.

It is not possible to change a property or call a method on the add-in until the add-in is ready and the page has been created.  So, here I solve this by using the Singelton class.  I make the Singelton add-in a reference in the Buttons & TextBoxes add-in and the Singelton class can be accessed the whole time.

In my NAV Page I added the Singelton NAV add-in as a client-side DotNet variable and added three lines of Singelton code.

SingeltonInit2

Similar in my Buttons & TextBoxes add-in I added two properties to read these Singelton values.

SingeltonGet

When the panel is created I make sure that I read these properties.

panelsize

It is important to use the XML format in NAV when using this Singelton Dictionary.  Make sure to always use FORMAT(<value>,0,9) and on the other side use EVALUATE(<value>,SingeltonValue,9).

Evaluate

 

 

The function call was ambiguous. No matching method was found.

One of the problems with using DotNet in C/AL is that sometimes the C/AL compiled does not have enough information for select between different methods in the DotNet object.

As you might have gathered from my last posts, I am using DotNet more and more.  It saves a lot of code writing and it is fast.

Today I was handling a web service that is delivering decimal value formatted with the is-IS method.  The decimal character is a comma.  I wanted to make sure that my C/AL code would be able to handle that no matter what the underlying language or region settings where.

Getting the decimal value was pretty straight forward.  Just used the method Microsoft uses in Codeunit 10 with a predefined is-IS.  Now for the other way around.  Found a method for the System.String to format a decimal to a text based on the same is-IS.

StringFormat1

But here comes the problem.

StringFormatError

Ok, Vjeko sais everything can be solved with reflection – well perhaps not everything, but this problem sure can.

I listed all the methods for the System.String and found the one I wanted to use.  Did not have to use the System.Activator since this is a static class.

StringWithReflection

Yes, a few more lines but they are effective and there is always fun to get challanged.  So, if you ever get this error message again, reflection should be able to solve the problem.

here is the ISLFormatAndEvaluate code to download.

Run a Table in NAV

One of the things we developers and consultants miss the most from the “Classic Times” is the ability to run a table to edit the data. Sure we can run a table from the Developement Environment but we are not always working with direct access to the SQL database.

My solution is to have a Page running on the object table (Table2000000001). From the page I can start Pages, Reports, Codeunits and XML Ports directly with a simple line of code, but to start a Table is more complex.

The first solution was to use HYPERLINK on the result from the GETURL function. That works fine in a local environment, but when you have installed multiple Dynamics NAV versions or running Dynamics NAV from a ClickOnce installation things start to break.

To fix this I stop using HYPERLINK and start the Dynamics NAV client with arguments to run a table. First step is to find the current client path. This path can be the usual System Drive path but it can also be a User Application Path if using ClickOnce. The function to locate the client path uses DotNet.

[code] PROCEDURE GetClientPath@1100408003() : Text;
VAR
ClientAssembly@1100408001 : DotNet "’mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Reflection.Assembly" RUNONCLIENT;
ClientPath@1100408000 : DotNet "’mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.IO.Path" RUNONCLIENT;
BEGIN
ClientAssembly := ClientAssembly.GetExecutingAssembly;
EXIT(ClientPath.GetDirectoryName(ClientAssembly.Location));
END;[/code]

And based on the client Path I look for the ClientConfiguration.config file and start the client with the GETURL results.

[code] LOCAL PROCEDURE ViewRecords@1100408000();
VAR
AddinMgt@1100408000 : Codeunit 10000207;
PathHelper@1100408008 : DotNet "’mscorlib’.System.IO.Path";
ClientFileHelper@1100408007 : DotNet "’mscorlib’.System.IO.File" RUNONCLIENT;
ClientProcess@1100408005 : DotNet "’System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Diagnostics.Process" RUNONCLIENT;
ClientProcessWindowStyle@1100408004 : DotNet "’System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Diagnostics.ProcessWindowStyle" RUNONCLIENT;
ClientProcessStartInfo@1100408003 : DotNet "’System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′.System.Diagnostics.ProcessStartInfo" RUNONCLIENT;
StartCommand@1100408002 : Text;
ArgumentList@1100408006 : Text;
ClientPath@1100408001 : Text;
ClientConfigurationPath@1100408009 : Text;
BEGIN
ClientPath := AddinMgt.GetClientPath;
ClientConfigurationPath := PathHelper.Combine(ClientPath,’ClientUserSettings.config’);
IF ClientFileHelper.Exists(ClientConfigurationPath) THEN
ArgumentList := STRSUBSTNO(‘-settings:"%1" ‘,ClientConfigurationPath);
ArgumentList += GETURL(CLIENTTYPE::Windows, COMPANYNAME, OBJECTTYPE::Table, ID);
ClientPath := PathHelper.Combine(ClientPath,’Microsoft.Dynamics.Nav.Client.exe’);
ClientProcessStartInfo := ClientProcessStartInfo.ProcessStartInfo(ClientPath);
ClientProcessStartInfo.Arguments := ArgumentList;
ClientProcessStartInfo.WindowStyle := ClientProcessWindowStyle.Normal;
ClientProcess := ClientProcess.Start(ClientProcessStartInfo);
END;[/code]

To repeat my last comment, just use DotNet 🙂

Just use DotNet

Day by day I am moving closer to DotNet programming in Dynamics NAV. More and more of the things I like to do are more easily solved with DotNet than with native C/AL code.

In most cases I can use the standard DotNet types but in some cases I need to build a small DotNet Class to solve the problem.

An example of this landed on my desk yesterday. A colleague of mine needed to be able to print text to the local label printer. Perhaps this can be solved with a simple report but in this case something more was needed. I asked the Internet – how do I print a text file with c# code. Got some answers and selected the one I liked the most.

Normally I rewrite the c# code with DotNet objects in C/AL but in this case I could not. The reason was that I needed the DotNet objects to be executed on the client side, and using DotNet to print requires an event handler. DotNet events are not supported on the client side so I needed to create a class.

I create a c# Class Project in Visual Studio and can use the code I found with only a few modifications.

[code lang=”csharp”] public class NAVTextFilePrinter
{
public void PrintText(string printerName, string fontName, float fontSize, string[] linesToPrint)
{
Font printFont = new Font(fontName, fontSize);
PrintDocument docToPrint = new PrintDocument();
docToPrint.DocumentName = "NAV Text File";
docToPrint.PrinterSettings.PrinterName = printerName;
docToPrint.PrintPage += (s, ev) =>
{
int count = 0;
float yPos = 0;
float leftMargin = ev.MarginBounds.Left;
float topMargin = ev.MarginBounds.Top;

foreach (string line in linesToPrint)
{
yPos = topMargin + (count * printFont.GetHeight(ev.Graphics));
ev.Graphics.DrawString(line, printFont, Brushes.Black, leftMargin, yPos, new StringFormat());
count++;
}
ev.HasMorePages = false;
};
docToPrint.Print();
}
}
[/code]

The class is compiled with DotNet 4.5 and added to the Client Add-ins folder. If you are using NAV version 2015 or newer just add it to the Server Add-ins folder.

Using this in NAV is easy

PrintNAVText

Microsoft.Dynamics.Nav.Client.TextFilePrinter can be downloaded from here.

I like to use DotNet when I am handling text – in general. The DotNet object System.String has a lot of functions that can be useful. Just use

String := String.Copy(NAVString);

and your NAV String can now be handled with all the functionality available with DotNet.  Good example is the Renumbering Tool I created a few months ago.

Another example here – where I need to create all the directory tree leading to the file I need to copy.

CreateDirectoryTree

One of my favorite is the Global Variable Store Codeunit, where I use the DotNet Dictionary to store variables globally. This can be used to minimize the footprint of your code changes. For example, if you need to pass a new variable to a function and you don’t want to change the function – just store the variable before you call the function and retrieve it inside the function. It is even possible to pass a whole record this way.

Downloadable Global Variable Store Codeunit

 

Clean up your trail

Boy, have these last months been busy.

Now I am working on a management solution for our cloud offering.  This solution is going to give the control of the services and the tenants to a NAV user interface.  I am running Powershell scripts from NAV (thanks Waldo) and things are looking good so far.

I extended the Powershell functionality to be able to read an XML response like Waldo describes here.

One of the things I need to do is to maintain files for this management solution.  For example a NAVData file, SQL backup file and a NAV license file.  When ever I execute a Powershell script from NAV I write these files to a temporary file path and point Powershell over there.

I can’t be sure that every Powershell execution is a success and I can’t leave the temporary files around.  I must delete them.  The solution in my case is to create a single instance Codeunit and apply a DotNet List object.

I only needed a server version, but with DotNet this can be extended to handle the client side as well.

VariableStore

 

So, every time I create a temporary file I add the file to a DotNet List.  Even if the execution fails the temporary file is still in the list.  When the execution finishes all the listed temporary files are removed.  If the execution fails then the next successful execution will also remove the previous temporary file.

As you can imagine this type of a Codeunit can be used in many scenarios.  By using DotNet Dictionary it is easy to store parameters with names in one place and retrieve them in another.

For example if you need to add a parameter to a standard function, then adding the parameter to the Dictionary before you execute the function and retrieving it within the function will leave the function footprint unchanged and your customization upgradeable.