Getting Started with an Add-in Assembly for Microsoft Dynamics NAV 2009 R2

One of the new key features for Microsoft Dynamics NAV 2009 R2 is the interoperability extension with the .NET Framework, also called the DotNet interop feature. This feature allows the AL code to call out to .NET types and use the exposed methods and properties. The types can be system types installed with the .NET Framework, or other applications that register assemblies in the Global Assembly Cache (GAC), or it can be custom assemblies that are stored in subfolders relative to the Microsoft Dynamics NAV server or client.

This article will in a few words explain how to get started with an assembly that will be stored in the Add-in folder and the target audience is intended to be the AL developer that has no or very little experience with programming in the .NET environment.

Introduction

The following tasks will be described:

  1.  Implement and compile a demo assembly (with or without Visual Studio).
  2.  Deploy the compiled assembly.
  3.  Develop and test the demo assembly.
  4.  Improve deployment and security.

The demo assembly that will be used in this article will be kept very simple and is only intended as a visualization of the principles.

Implement and Compile a Demo Assembly (with or without Visual Studio)

The demo is based on a small assembly that only implements one type (class) that contains properties, a constructor and a method. Use your favorite text editor to create the C# file “DemoAddin.cs” containing the following code:

// Copyright © Microsoft Corporation. All Rights Reserved.
// This code released under the terms of the
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)

namespace DemoAddIn
{
    using System;
 
    public class DemoAddIn
    {
        /// <summary>
        /// Private field where the constructor stores the name of the 
        /// demo object.
        /// </summary>
        private readonly string demoName;
 
        /// <summary>
        /// Gets the private read only field demoName.
        /// </summary>
        public string DemoName { get return demoName; } }
 
        /// <summary>
        /// Backing field for the property
        /// </summary>
        private string getSetProperty;
 
        /// <summary>
        /// Gets or sets a property using a backing field. Don’t return null values to AL
        /// as this can give unpredictable results.
        /// </summary>
        public string GetSetProperty
        {
            get return getSetProperty ?? String.empty; }
            set { getSetProperty = value; }
        }
 
        /// <summary>
        /// Initializes a new instance of the DemoAddIn class and assign a value to the
        /// demoName field.
        /// </summary>
        /// <param name="name">Name of the demo object.</param>
        public DemoAddIn(string name)
        {
            demoName = name;
        }
 
        /// <summary>
        /// Sample function that illustrate how to use function parameters and 
        /// return data.
        /// </summary>
        /// <param name="arg">Function parameter</param>
        /// <returns>The function arguemnt value or the constant string Empty if the 
        /// parameter is null or empty.</returns>
        public string FunctionWithArgument(string arg)
        {
            return String.IsNullOrEmpty(arg) ? "Empty" : arg;
        }
    }
}

The assembly can be compiled from any C# compiler, and two common setups are shown below:

Visual Studio

Create a new solution and project named “DemoAddIn”. The project must be created using the template type “Class Library”. By default, you will have a file called class1.cs, which can be deleted from the project. Add a new C# file to the project and implement the class listed above. It should not be necessary to change any project options and when you can build the output successfully, you are ready to deploy the assembly to the NAV clients and server.

Note: If you use Visual Studio 2010, please make sure that the target framework is set to .NET Framework 3.5 or 2.0.X. Microsoft Dynamics NAV 2009 R2 does not support assemblies compiled for .NET Framework 4.0. The framework version is set using the project properties and can be found in the Application tab.

The compiled output will be stored in a folder named “bin\Debug” beneath the project folder.

.NET Framework Compiler

The framework compiler is a simple command line tool that is installed together with the framework and is available on all machines that uses .NET Framework.

  1. Open a command prompt and navigate to the folder containing the DemoAddin .cs file
  2. Compile the file using the command (replace Framework64 with Framework if the compilation takes place in a 32 bit OS).

%windir%\Microsoft.NET\Framework64\v2.0.50727\csc.exe /debug /target:library DemoAddIn.cs

This will create two files in the current directory.

  1. DemoAddIn.dll, which is the assembly
  2. DemoAddIn.pdb, which contains symbol and debug information

When the assembly is final, then remove the /debug flag and add the desired optimizations. If the symbol file is desired, then add the option /debug:pdbonly.

Deploy the Compiled Assembly

The two files generated in the previous steps have to be deployed to the Add-ins folders within the product folders (it’s most convenient if the files are stored in a subfolder within the Add-ins folder; use the name DemoAddIn for the target folder):

  1. Design time using the Microsoft Dynamics NAV Classic Client.
    %ProgramFiles(x86)%\Microsoft Dynamics NAV\60\Classic\Add-ins\DemoAddIn
  2. Server service
    %ProgramFiles(x86)%\Microsoft Dynamics NAV\60\Service\Add-ins\DemoAddIn.
  3. The Microsoft Dynamics NAV RoleTailored client
    %ProgramFiles(x86)%\Microsoft Dynamics NAV\60\RoleTailored Client\Add-ins\DemoAddIn.

The environment variable %ProgramFiles(x86)% will point to the Program Files folder that is used for 32 bit applications in a 64 bit operating system. If the current operating system is 32 bit, then use %ProgramFiles%, which by default points to C:\Program Files.

Deployment during development of the Add-in assembly will be easier if a script like the following is created in the project folder:

@echo off
setlocal

set AssemblyBase=DemoAddIn

set root=%ProgramFiles(x86)%
if “%ProgramFiles(x86)%” == “” set root=%ProgramFiles%

call :deploy %AssemblyBase% “%root%\Microsoft Dynamics NAV\60\Classic”
call :deploy %AssemblyBase% “%root%\Microsoft Dynamics NAV\60\Service”
call :deploy %AssemblyBase% “%root%\Microsoft Dynamics NAV\60\RoleTailored Client”
goto :EOF

:deploy 

set AddInfolder=”%~f2\Add-ins\%~1″
if not exist %AddInFolder% mkdir %AddInFolder%
if exist %~1.dll copy %~1.dll %AddInFolder%
if exist bin\debug\%~1.dll copy bin\debug\%~1.dll %AddInFolder%

if exist %~1.pdb copy %~1.pdb %AddInFolder%
if exist bin\debug\%~1.pdb copy bin\debug\%~1.pdb %AddInFolder%

goto :EOF

It will copy the assembly and symbol files to the respective Add-in folders. If the project is being managed by Visual Studio, the script can be called from the PostBuildEvent so the binaries are deployed automatically on every successful build. Notice that the NAV server and client must be stopped before a new version is deployed as the application will keep the files locked until they are stopped. It’s not possible to update any Add-in assembly while a previous version is loaded as the OS will lock the files as soon as an application has loaded it. Adding this to the deployment script can be an option, but care should be taken so data isn’t lost (don’t kill the wrong client or server).

Develop and Test the AL Codeunit that Uses the Demo Assembly

Create a new codeunit in the Classic client and name it ‘Demo AddIn’ (use a free objectid in the 50.000-100.000 range).

In the OnRun trigger create the following variables:

Name DataType Subtype Length
demoAddIn DotNet DemoAddIn.DemoAddIn
demoString DotNet System.String (mscorlib)
textVar Text 512
intVar Integer

Note: for the Subtype of the DotNet DataType variables, use the assembly picker to select the DemoAddIn assembly from the “Dynamics NAV” tab and choose the type DemoAddIn.DemoAddIn from the “.NET Type List” that appears when the assembly is selected.

The sample code utilizing the demo AddIn can be implemented as:

// Copyright © Microsoft Corporation. All Rights Reserved.
// This code released under the terms of the
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)

// Create an instance of the DemoAddIn type. The constructor takes a string argument
// which is used to initialize an internal variable.
demoAddIn := demoAddIn.DemoAddIn(‘Input data’);

// Use the property ‘DemoName’ to read the name set in the constructor.
MESSAGE(‘DemoAddIn contains  %1’, demoAddIn.DemoName);

// Test that the property ‘GetSetProperty’ contains an empty string
if not demoString.IsNullOrEmpty(demoAddIn.GetSetProperty) then
  error(‘The uninitialized property should return an empty string’);

// Use the property setter to assign an AL string to the internal CLR string variable.
demoAddIn.GetSetProperty := ‘Value set with property’;

// Read the property and see that the value has been set accordingly.
MESSAGE(‘DemoAddIn contains  %1’, demoAddIn.GetSetProperty);

// Call a function in the AddIn that takes a string argument and returns the string
// given in the argument.
textVar := demoAddIn.FunctionWithArgument(‘Using FunctionWithArgument to change data’);
MESSAGE(‘Argument data is %1’, textVar);

Compile the codeunit in the classic client and run it through the Microsoft Dynamics NAV RoleTailered client (RTC). As it’s hard to run codeunits from the RTC client, it can be convenient to create a small page object from which a codeunit can be run.

The sample primarily uses types from the demo add-in, but uses also a method from the CLR String class to check the validity of a string value. Typical scenarios involve types from both user defined assemblies and the Framework libraries (very often from mscorlib, System, System.Xml).

Example: Search for System.String and select the first entry in the search results (will be “String Class (System)”). From the screen shot below, it’s seen that the String type is defined in the assembly “mscorlib”.

Suggested Reading

The MSDN documentation for Microsoft Dynamics NAV R2 contains an Update guide which documents the .NET interop feature in R2 and gives a couple of examples of simple use cases.