From C/AL to executable: how NAV runs your C/AL code

  • Post category:Platform
  • Post comments:22 Comments
  • Reading time:12 mins read

A lot of folks write C/AL and never worry about what happens then. C/AL is written, NAV execute is, the story ends. The same way the story ends when you flush a toilet and the tank refills. How exactly? Who cares.

While understanding the inner workings of a toilet flush tank doesn’t necessarily make you more efficient at whatever it was that made you press the flush button in the first place, having a better understanding of exactly how NAV uses your C/AL code throughout its lifecycle is of arguably higher practical value.

Have you ever wondered what exactly happens to your C/AL code when you write it? How exactly does NAV run that stuff? Does it do any run-time interpretation, or does it compile C/AL into native code that runs on a processor? What does just-in-time compilation  mean and does it happen with C/AL? If so, when and why?

If any of these questions bother you, read on. If they don’t bother you, read on because they should bother you. If they don’t bother you because you know the answers, read on still, and then brag by poking holes in my explanation.

First of all, NAV does not run C/AL code. That should be obvious. It does not run C/AL, it does not interpret C/AL.

All of NAV runs as a managed .NET application, which means that all code that NAV executes must have been written in a managed .NET language. Since C/AL is neither a .NET language, nor is it managed by anything in terms of .NET Common Language Infrastructure (CLI), obviously something else is going on in there.

There are two stages on what’s going on before .NET CLI even gets anywhere close to your code.

The first stage has to do with the C/AL code and the purpose it serves ever since 2013 (or 2009 as far as three-tier architecture was concerned). When you compile your object, your C/AL is translated into C# and from then on, your C/AL code does not matter any more. C# is the only thing that matters.

To make this part of the story short, the C/AL compiler simply translates C/AL into C#.

Well, duh! This has been a well known fact for eight years already, what’s the big deal here? Not much, but you might want to read on, still.

How and where is this C# code stored? In the old times, there was the Object table that contained everything you needed to know about objects. However, nowadays we have another table you might want to take a look at: 2000000071 Object Metadata. This table contains two BLOB fields: User Code, and User AL Code.

User AL Code is your C/AL code. As I said, once you compile it, whatever there is in this field is going to be completely ignored by the NAV runtime.

What matters is the User Code field. It contains the C# translation of your C/AL code, and if the object has the Compiled flag on in the Object Designer, it’s the content of the User Code field that matters to the NAV runtime.

And this is how it matters.

When you run an object, the content of the User Code field is saved on the disk as a C# file. The location is \ProgramData\Microsoft\Microsoft Dynamics NAV\<version>\Server\MicrosoftDynamicsNavServer$<instance>\source\<object type>.

If you check that location, you’ll see a bunch of C# files. The content of these files was first created when the object was compiled, and was written to the disk when the object was first run.

(There is another situation when all C# files will be written to the disk at a server instance startup, but that’s not a topic of this post.)

The next thing that happens when you run your object is that C# compiler kicks in. It takes your C# file and compiles it into a .NET assembly. This is not a just-in-time  compiler, it’s just C# compiler that generates a .NET assembly. That’s a prerequisite for any .NET thing to run. Just-in-time compiler is an integral part of the CLI, it happens way further down into the lifecycle of a loaded assembly and has nothing to do with any kind of compiling that you do, or the NST does while either translating your C/AL into C# or C# into MSIL (or CIL if you want to be more precise).

Once the .NET assembly (DLL file) is created, NAV loads it runs it.

Exactly how NAV runs it is not at all an NAV thing anymore, it’s entirely .NET internals that govern this process. For the sake of simplicity, let’s just say that at this time, CLI will invoke the just-in-time compiler that will convert the bytecode of the assembly into the native code of the processor which is executing, which is always x64, and then the processor executes this native code.

Seems fairly simple, but it’s not. Because things can go wrong where you’d expect no things to go wrong.

For example, occasionally, you may run into an error message such as this:

Image 3-3

This class of error messages is among the most confusing error messages a user can get from the NAV runtime. It’s not quite a compile-time error, because – as far as you are concerned – your object was already compiled, and it compiled without errors. And it’s not quite a runtime error, because it happens before your runtime starts executing your code. Technically, though, this is a runtime error, not a compile time error. It’s not you who gets it, it’s your users who get it (actually, they don’t quite get it, they only get to see it).

With error messages there is an inverse proportional rule at play: the longer it is, the less likely people will read it, let alone understand it.

When they see this, your users will not have a faintest idea what went wrong. It mentions the “compilation”, “assembly”, “binaries” and other spooky words your users won’t understand.

Since it mentions “compilation” you may want to fix the issue by recompiling the object. Depending on the root cause of the problem, this may, or may not solve this problem.

There are three root causes of this problem, so let’s take a look at them.

First of all, what exactly does this error message say? If you read carefully, you’ll figure out that it, in fact, tells you that NAV was unable to compile your C# into a DLL.

The first root cause of this is most likely your case, and it’s explained right there in the error message (albeit in a language that requires a lawyer to interpret correctly): your NAV runtime version may be different than the NAV version used to compile the C/AL object into C#.

Since translation of C/AL to C# is governed by the development environment, and since users cannot influence how exactly this will be done, it’s entirely up to the Development Environment to perform this. And it does it by applying set of rules coded into its logic. And this set of rules may change between NAV versions. Hint: it does change, quite a bit. It not only changes between major versions of NAV, like 2013 to 2015; no, it sometimes also changes between minor versions, like CU1 to CU2 to CU3.

While these changes may not be major, they can have an effect on validity of C#. A piece of C# code generated  under 2015 CU5 may not be entirely valid under 2015 CU7 (giving versions only as illustration, not saying that there is any particular difference between these particular versions). If C# that the compiler generated bona fide (that you will further run this object under the same runtime version) refers to a specific platform feature not available under the runtime version you are using to run the object, you’ll get the error message as the one above.

Take a look at this:

SNAGHTML84b6376

There is nothing wrong with C/AL in here. What’s wrong is that obviously the C# file refers to a feature (GetFieldRefSafe) which was available to the platform version used to compile C/AL into C#, but unavailable to the platform version used to compile C# into MSIL.

(This is not a just-in-time compiler error, mind you! This is merely a C# compiler error.)

If this is your cause, then you are lucky. Simply go back to the Development Environment, make sure it’s build number matches the build number of your NST (in other words: that they are the same platform; or in even more other words: that the binaries of your development environment match the binaries of your NST installation). Then, if they do match, simply recompile the object. This will re-translate C/AL into C#, applying the rules for the current platform, and this will create a C# that can be compiled by NST at run time, without issues.

Another root cause of this problem is .NET interoperability. There are situations where a perfectly valid C/AL will translate into what at face value appears to be a perfectly valid C#, except that there will be a runtime type mismatch for types which looked okay at compile time. Take a look at the first error again:

image

If this is what’s causing you problems, then unfortunately you are out of luck. You can compile your C/AL as much as you want, you’ll always get this. An example of offending code is this:

image

(DotNetDateTime being of DotNet type, System.DateTime subtype, and CALDate being of Date type)

Again, your C/AL code is valid here. Not entirely according to the best practices, but still valid. To make it work, you need to do some refactoring. Depending on exact types involved, or exact operation involved, this may be simple, not-so-simple, or outright complicated.

The third root cause of this error message is something I’d not like to go into at this stage – I am keeping it for a blog post that I will put here, only not quite yet (otherwise, it’d spoil something else I am working on). But when I do blog about it, you’ll know.

In any case, this third root cause is simple to solve – you recompile the object and you are good to go.

To wrap it up: C/AL is translated into C# when you compile the object. C# is translated to MSIL, then to bytecode when NAV compiles C#. Bytecode is translated to native code when just-in-time compiler is invoked by CLR when NAV wants to run the assembly for the first time. Processor then executes the native code.

I know that most bloggers like talking about new things. What I talked about here is not new things, it’s old things. However, I still thought explaining this here would help a lot of people. If it helped you, I’m glad. If not, just flush it down the toilet.

Vjeko

Vjeko has been writing code for living since 1995, and he has shared his knowledge and experience in presentations, articles, blogs, and elsewhere since 2002. Hopelessly curious, passionate about technology, avid language learner no matter human or computer.

This Post Has 22 Comments

  1. Vytenis Jakas

    This could be a turned into a nice documentation page for hunting down obscure NAV runtime errors. Good read!

    P.S. Erm, did you just compare writing NAV code to the process that happens before the toiled flush? 🙂

    1. Vjeko

      Nope, I did not. Geez, Vytenis 🙂

  2. Gintautas

    Another example to your error list would erros caused by ‘funky’ characters in C# files:
    https://s21.postimg.org/yob2n0miv/IMG_05102016_093013.png

    We got this error due to harcoded text characters in C/AL code. Code runs fine on your local pc with your regional settings. Once you take your code to the customer environment running on Chinese (or any other strange language), you get into this trouble. Harcoded string becomes something else under different regional settings.

    Solution: never hardcode text strings! If needed, use TextConstant instead. TextConstants are stored as string of bytes in C# and are not affected by regional settings changes.

    1. Vjeko

      I am not quite sure it’s an error of the same kind, it smells so, but it may be a tiny but different as well. Thanks for contributing, Gintautas. I have never encountered this one myself.

  3. Natalie

    I prefer such a “old things” post over those containing the same tiny piece of new information repeated by 100 bloggers at one time. Well done, and see you in Antwerp!

    1. Džoka Mitrov

      so true!

  4. Džoka Mitrov

    A good read!

    I ran in one of those cases just a few days ago where I had to refactor COM Automation Code (actually change the COM component) because of older NAV2009 R2 builds in the wild. The error message showed up only at runtime ‘The metadata for the object are invalid yadda yadda… invalid arguments… yadda yadda’

  5. Gianluca Iumiento

    Great post Vjeko. I had investigated object metadata to understand better this process and how c/al code is converted into c# code. Who knows if in the future they’ll just convert the code and let us directly code in c# rather then c/al. It might be a bit too dangerous but it will open a world of possibilities. Don’t know if you’re blogging on that in the future so I am not going into many details but I find very interesting also the content of Metadata blob field. I have been playing with that with some objects and being not fully protected like User code fields it opens interesting options.
    I was wondering if the compilation from c/al to c# is triggered only by first usage of the object by a session or if there is some precompiling happening because that might affect performance.
    Thanks again for your interesting posts

    1. Vjeko

      Thanks, Gianluca! I will indeed blog more about this, so thanks for leaving any spoilers out 🙂

      Regarding compilation from C/AL to C# – it does not happen when you run the object. It only happens when you compile the object in the Development Environment.

      As far as I know, there is no pre-compilation going on at any point, C# is compiled when the object is first run. You should not worry about performance issues, it’s blazing fast and doesn’t cause perceivable downtime. More time is spent to stream C# out of the database onto disk than it takes to compile it.

      1. BlueInTheSky

        Thank you, Vjeko, for this really nice post !
        I tried to play with it to understand, how NAV creates C# .cs and .dll-Files for the complete C/AL code (so all NAV-objects).
        So I created a new NAV2017-Cronus-Database, set “Enable Debugging” in the ServiceTier, compiled all objects, restarted the ServiceTier and looked what happend in the directory
        C:\ProgramData\Microsoft\Microsoft Dynamics NAV\100\Server\MicrosoftDynamicsNavServer$DynamicsNAV100.
        First, I looked in the subfolder \source :
        At the beginning it looked like there would be a corresponding .cs-file for all nav objects. At second glance, however, you could see that there were no (or very few) .cs-files for certain object types, especially for the object types: Report, XMLport, Query and MenuSuite.
        Second, I looked in the subfolder \assembly :
        To look inside the dll-Files there I opened them in the freeware-Tool ILSpy (https://github.com/icsharpcode/ILSpy). But also there I couldn’t find traces from the missing objects.
        So I created an individual table 50000, page 50000, report 50000, codeunit 50000, query 50000, XMLport 50000 and Menusuite 1090, compiled them, restarted the ServiceTier and looked again in the subfolders.
        The result: I could find corresponding c#-code for table 50000, page 50000 and codeunit 50000, but not for: report 50000, query 50000, XMLport 50000 and Menusuite 1090.

        Do you have any ideas how to get to the C # code / C # assemblies for the complete C / AL code?
        I would like to analyze the topic more closely and I am grateful for any hints / sources.

        Thank you in advance and best regards,
        Blue

        1. Vjeko

          Hi,
          I am not sure why these files you want to find are not there. It may be that Microsoft changed something about 2017, but don’t take my word for that. I hope you understand that I don’t have time now to install all the old versions to test what’s going on in there, and what’s created and what not. If the files you expect are not there, then it may simply be that for some reason NST isn’t creating them. Maybe it will create them on demand when you first try to run any of them. Don’t know.

  6. Luc van Vugt

    Great read and making our fundament more clear. Focussing on new things is sometimes over rated.

  7. Yuriy

    It is very good explanation how NAV compiller works.
    For last versions of NAV we have RDLC Report and before system runs explorer with view Dynamics NAV generates ready-to-use “XML document”, right?
    May be you know is it possible to catch somehow this XML-“file” before Preview|Printing and to see (may be to update) all data (and, possible, structure)?

    Thank you in advance for your answer.

    1. Vjeko

      Sorry, I don’t know of a way to intercept that XML. It’s not to say it’s not possible; it’s just that I don’t know how to.

      1. Yuriy

        Thank you and I will try to find how to do it..

  8. BlueInTheSky

    Thank you, Vjeko, for this really nice post !
    I tried to play with it to understand, how NAV creates C# .cs and .dll-Files for the complete C/AL code (so all NAV-objects).
    So I created a new NAV2017-Cronus-Database, set “Enable Debugging” in the ServiceTier, compiled all objects, restarted the ServiceTier and looked what happend in the directory
    C:\ProgramData\Microsoft\Microsoft Dynamics NAV\100\Server\MicrosoftDynamicsNavServer$DynamicsNAV100.
    First, I looked in the subfolder \source :
    At the beginning it looked like there would be a corresponding .cs-file for all nav objects. At second glance, however, you could see that there were no (or very few) .cs-files for certain object types, especially for the object types: Report, XMLport, Query and MenuSuite.
    Second, I looked in the subfolder \assembly :
    To look inside the dll-Files there I opened them in the freeware-Tool ILSpy (https://github.com/icsharpcode/ILSpy). But also there I couldn’t find traces from the missing objects.
    So I created an individual table 50000, page 50000, report 50000, codeunit 50000, query 50000, XMLport 50000 and Menusuite 1090, compiled them, restarted the ServiceTier and looked again in the subfolders.
    The result: I could find corresponding c#-code for table 50000, page 50000 and codeunit 50000, but not for: report 50000, query 50000, XMLport 50000 and Menusuite 1090.

    Do you have any ideas how to get to the C # code / C # assemblies for the complete C / AL code?
    I would like to analyze the topic more closely and I am grateful for any hints / sources.

    Thank you,
    Blue

  9. Thomas Tarp

    Hi Vjeko,
    Do you know if there is an equivalent of this for AL?

    1. Vjeko

      Sorry, I don’t have an equivalent post for this for AL.

Leave a Reply