Dynamics 365 Business Central (on-premise): Extension for printing external documents

A week ago I wrote a post for answering a question regarding how to print external documents from Microsoft Dynamics NAV. This code is C/AL based and it uses DotNet classes that interacts with the local operating system.

How can this code be translated as Extension for Dynamics 365 Business Central?

For the SaaS version of Dynamics 365 Business Central, this is actually not possible. We can’t use DotNet objects in the cloud. But what about the future on-premise version?

This is currently in preview (so you don’t find this feature in your standard sandbox environment) but starting from build 13.0.22895.0 you can call .NET types from AL code if you’re targeting an on-premise deployment.

To use a .NET type in AL, you need to start by declaring a dotnet package:

dotnet 
{ 

}

then you add a declaration of the assembly that you want to reference (one or more than one):

dotnet 
{ 
  assembly(YOURASSEMBLY) 
  { 

  } 
}

and finally you have to add a reference to a .NET type from the referenced assembly (by using the .NET fully-qualified name and giving it an alias that you will use for referencing the .NET type from AL code):

dotnet 
{ 
  assembly(YOURASSEMBLY) 
  { 
    type(YOURDOTNETTYPE; ALIAS) {} 
  } 
}

So, how can we transform our previously developed C/AL code for external document printing to be used in AL for the next on-premise Dynamics 365 Business Central version?

The first thing to do is starting a new AL project with Visual Studio Code, open the app.json file and insert the following line:

DotNetAL_01.jpg

This means that your extension is targeted for the on-premise deployment.

Then (extremely important!), you need to instruct the compiler where it has to search for assemblies. By default, the compiler only knows the location of the mscorlib assembly. To add new assembly paths for the compiler, you need to open Visual Studio Code user settings and add the following lines:

DotNetAL_02.jpg

The “al.assemblyProbingPaths” setting instructs the compiler where to search. Restart Visual Studio Code after adding this setting.

After that, you can start coding. Exactly like in the C/AL sample, I have a codeunit that does all the tricks. In my AL project, I’ve created a codeunit called DotNetWrapper that:

  1. Declares the DotNet assemblies that I need to use
  2. Declares the functions that uses the DotNet variables and performs the actions

The AL codeunit is as follows. First, we declare our .NET assemblies:

DotNetAL_03.jpg

and then in our functions we can use the .NET types by declaring DotNet variables:

DotNetAL_04.jpg

Having DotNet variable support for the on-premise is a must and now we have a great way to handle these scenarios. What about the SaaS? Nothing change for the moment 🙂

P.S. a small note about a noisy effect I’ve observed in Visual Studio Code when declaring the dotnet package: VS Code could give you an error indicating that the .NET assembly cannot be located. First, restart VS Code. If the error persists, just force a compilation and it will magically disappear.

21 Comments

  1. Hey guy, I followed your instructions, but still I am not able to create objects of the DotNet data type. Visual Studio Code permanently shows me the message: “[AL] This feature is under. It can be enabled by using the dotnet feature flag.” Do you know how to solve this?

    Like

  2. At the moment I am working only locally, that is why I have no BC yet. O I need this version, even if I only want to create locally on my computer objects of data type DotNet without using BC?

    Like

  3. Now I understood^^
    Do you also know, whether we can generate objects of the data type automation with the new version?

    Like

  4. If you run this code (F6) against the 13.0.24623 you get a nice error

    Something went wrong.
    Er heeft zich een fout voorgedaan
    Date and time: Mon, 01 Oct 2018 13:38:28 GMT

    At that point you’re just starting up the webclient, so the issue is releated due to
    dotnet
    {
    assembly(System)
    {
    type(System.Diagnostics.Process; Process) { }
    type(System.Diagnostics.ProcessStartInfo; ProcessStartInfo) { }
    type(System.Diagnostics.ProcessWindowStyle; ProcessWindowsStyle) { }
    }
    }

    or

    If you debug this code (F5) against the 13.024623 you get the rolecenter.
    Running the code doesn’t do anything, no error, no printing.

    Even tried with the runonclient

    local procedure PrintDocument(Path: text)
    var
    ProcessInst: dotnet Process runonclient;
    ProcessStartInfoInst: dotnet ProcessStartInfo runonclient;
    ProcessWindowStyleInst: dotnet ProcessWindowsStyle runonclient;

    Same behaviour

    Like

  5. Thanks for the information. I followed your instruction and it worked great but found out a few days later that for some reason the dotnet definition included (identical to yours) breaks user personalization and I cannot figure out why. Any Ideas?

    Like

      1. It is very strange and I cannot figure out how the two are related. Basically if I have the dotnet lines enabled and run my extension and click on personalize anywhere in the system it brings up a message box “Personalization could not be enabled due to technical issues. Try again later, or contact your system administrator.”

        Commenting out the lines fixes the issue. We are using a third party extension (PrintVis) as a base so I was wondering if it was conflicting somehow with something they have done but so far it doesn’t make much sense.

        We also already use System.Data from botnet elsewhere in our project with no issues.

        Like

      2. We just resolved it. We found when we had a dotnet definition in two files it would cause this issue so merging the two definitions solved it.

        Like

    1. This code was an old code for BC 13. It uses .NET variables to call the Print verb of the server OS. Never tested with next releases. The file path must be reachable from the server (service tier).

      Like

  6. We would like to continue using .NET as an end customer because we use a lot of REST APIs in .NET. With “OpenAPI” and nswag it is very convenient to use REST APIs through InterOp in NAV.
    Without .NET, we would have to manually rewrite everything in AL and that would be much more time-consuming in the future.
    Does the Universal Code Initiative also affect end customers? Do end customers also have to pay a penalty if they want to continue using .NET?

    Like

    1. You can continue to use .NET components but your AL code calling this component should be transformed as “Universal Code” compliant in order to avoid extra fees in the future. This is honestly quite easy to do. You can continue to use your existing AL component, just wrap it into something that it’s SaaS-compliant. Create a local API that calls your .NET component and then call this local API from AL.

      Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.