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:
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:
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:
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:
(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.