Coding with C/AL
The C/AL programming language in Microsoft Dynamics NAV 2016 is similar to the older version of NAV. Coding with C/AL has always been easy because of one extra layer of preprocessing before all of its code is literally converted into C# code; it then follows the same compilation process as any other .NET program. A mediocre programmer can easily understand the previous code and make some changes in it. This is one of the reasons for the popularity of Microsoft Dynamics NAV among developers and consultants.
Connectivity of NAV with SQL is governed by the .NET standards. Especially, the interoperability with other third-party programs outside C/AL is directly controlled by the .NET architecture. This is also one of the reasons we need to install the .NET prerequisites into our system before installing Microsoft Dynamics NAC.
Later in this chapter, we will discuss how the codes are preprocessed into C# code, which all NAV developers need to understand.
Reusing code
One of the main intentions behind this chapter is to provide the reader with some guidance towards Good Programming Practice (GPP), and one of the best ways to follow the guidelines of GPP is to reuse the code. Reusing code makes developing applications faster and easier. In addition, it also saves memory space when the code is passed to the compiler for compilation by reducing the amount of code needed to be processed by the compiler system. If you organize your C/AL code as suggested by NAV standards, then your applications will be less prone to errors. By centralizing the code, you will not unintentionally create inconsistencies by performing the same calculations in many places, for example, in several triggers that have the same table field as their source expression. If you have to change the code, you could either forget about some of these triggers, or make a mistake when you modify one of them.
Note
Code reuse has been practiced from the earliest days of computer programming. Programmers have always reused sections of code, templates, functions, and procedures. This significantly helps if you are customizing code.
Best programming practice for C/AL code
There are many standards available in different fields of computer science when it comes to coding in a system. All standards feature the core objectives while dealing with the code part. Here are a couple of the main points that I would like to put forward:
- Always prefer using the codeunit to write code instead of on the object (page/table/report) on which it operates. This promotes a clean design, and provides the ability to reuse code. It also helps enforce security and also boosts the speed of code during code compilation.
- In case you need to put some lines of code on an object instead of in a codeunit, then put the code as close as possible to the object on which it operates. For example, put code that modifies records in the triggers of the table fields.
Multi-language functionality
Microsoft Dynamics NAV 2016 has a multi-language feature. This was one of the most discussed topics among Microsoft Professionals and clients, which is smartly presented in this release. The main idea behind the multi-language feature is to make translation easier between different languages, and allow it to switch between different languages at the level of the user interface.
Note
All C/AL code should be entered as English (United States). The main benefit of using the same language for code is to provide a smooth maintenance process. It also helps to keep different add-ons as the base for translation.
Activating objects
One of the most general errors that you get is a less informative error message in case of null values. It is important that we use relevant patterns of programming in computer science. In computer science, we are supposed to activate and test the variable before using it. It is the same in C/AL programming as well. Here we use the TESTFIELD
function to actually test the value of the variable we are going to use. The system can provide better informative error messages, and hence prevent extra error handling. It also allows a simple level of exception handling if the value is null or blank. This is depicted in the following code:
GLEntry.TESTFIELD("Department Code"); Dept.GET(GLEntry."Department Code"); GenJnlTemplate.TESTFIELD("Report ID"); REPORT.RUN(GenJnlTemplate."Report ID")
The Dialog.OPEN function
You should use the Dialog.OPEN
function only to indicate that the system is doing something, such as a process in progress:
Syntax: Dialog.OPEN(String [, Variable1],...)
If possible, use a progress indicator.
Example of a progress indicator
The following example explains how progress indicators can be implemented into your solution. It is always recommended when you are dealing with slow reports or processes which cannot run in the background:
MyNext := 0; MyDialog.OPEN(Text000,MyNext); REPEAT // Do some processing. MyNext := MyNext + 1; MyDialog.UPDATE(); // Update the field in the dialog. UNTIL MyNext = 9999; SLEEP(1000); MyDialog.CLOSE()
The following are the variables and constants used in the preceding code:
Text000
is a text constant, which progresses from 0 to 9999@1@@@@@MyDialog
is a value of theDialog
typeMyNext
is an integer variable
The following screenshot shows the output:
In Microsoft Dynamics NAV, the following are the recommended guidelines:
- Enter messages as text constants.
- Write messages using the active voice. For example, write
Processing items
instead of writingItems are being processed
. - Align the
#
and@
fields to the left with at least one space character between the text and the variable.
Tip
If you use the @
character instead of the #
character, then the variable value is used as a percentage, and both the percentage and a progress indicator are displayed. The percentage value that is displayed is the percentage of the variable value from 0 to 9999.
Some basic rules
Following are the most important rules you can use to achieve greater interactive messages for the end users. These not only provide information, but also establish the relationship between the user with the system:
- Always use active voice for messages. For example,
Processing items
. - Use
Text Constants
for messages. For example,Text001="This is a Message"
, and useText001
in the code instead of typing the message in the code. - End a message statement with an ellipsis for the running process. For example,
...
. - In order to insert some extra space, you can use two backslashes at the end of a line with the message. For example, :
- Text002=Batch Name 123456Checking lines #2######
- Always align an extra white space to the left of the field containing hash (
#
) and the longest text. Also put a whitespace between#
and@
. For example, refer to the example shown in the next section, Variables and constants. - The minimum length of the text field before
#
and@
must be 16, which includes the whitespace, which is always the final character before any of these symbols start, as explained in the previous point.
Variables and constants
The following example will explain all the rules in the preceding section. Here we can see how text constants can be used to make the code more efficient and clean. Also, the use of #
, @
, and are explained in the following example:
Window.OPEN(Text0001); //ConstValue = Processing items... Window.OPEN(Text0002) ConstValue=Batch Name 123456Checking lines #2###### @5@@@@@@@@@@@@@Checking balance #3###### @5@@@@@@@@@@@@@Posting lines #4###### @5@@@@@@@@@@@@@
Tip
Dialog windows that are opened by an object are closed when the object terminates. Dialog windows are automatically sized to hold the longest line of text and the total number of lines.
Avoiding deadlock
Certain rules must be observed to avoid deadlock and maintain consistency in the database. The Microsoft Dynamics NAV database has a deadlock protection, which prevents the entire system from locking up, as shown in the following diagram:
Note
In a computer program, deadlock is, effectively, a situation in which two computer processes or threads sharing the same resource/object prevent each other from accessing the resource, resulting in both the programs ceasing to function, and thus none being able to accomplish their task.
In day-to-day life, multiple users might try to access the same table (generally for posting purposes). There might be an issue because of this action at the system level, creating the problem of deadlock in the system. The following hierarchy must be followed in order to respect the locking pattern of the tables. Here I have made four categories into which the tables can be pided in order to prevent the system from getting into the state of deadlock. I highly recommend all coders to keep this in mind while dealing with table-level coding:
- Journals
- Non-posted lines and headers
- Posted lines and headers
- Ledger entries and registers
Locking orders
The following table explains the order in which the respective tables should be locked. Most programmers do not care about table locking, resulting in a bad experience for the end users in a multiuser environment:
Tip
The core of all these rules is to always lock tables at the lowest level first. If you can determine the lowest level, then you can easily understand this pattern.
Journals
In general, a journal has three levels: templates, batches, and lines. Templates are at the highest level, batches are at the middle level, and lines are presented at the lowest level, close to the user. In every process, journal records follow the locking pattern, where template, batch, and line act as the keys which can be used to better lock the record. During the deletion process of a journal, the Dynamics NAV system first deletes the journal lines, and then automatically locks them. It then repeats the process with the batch names, and finally, the template can be deleted. The pattern is the same for all journals, and if you customize some code, then you must pay close attention to this order.
Non-posted lines and headers
In the previous version of NAV, the database was different from the current version. We now have the standard MS SQL database, and now database consistency in the application is very important. In order to keep the records of the table consistent, you should always lock the sales line before locking the corresponding sales header table. This is crucial because of the structure of these tables and the manner in which they are linked with each other. When a user tries to push a record into a new sales line, the Sales Line
table is automatically locked. Since the tables are locked at this instance, none of the users can carry out delete operations on the sales header at that particular time. This is because the sales lines have to be deleted before a sales header can be deleted, which is again because of the structure in which the tables are linked. This is the core concept in all the Header line combination documents in NAV. Because of this, there will never be a sales/Purchase line without a corresponding sales/purchase header. This is the case for all other header-line document systems in NAV.
Posted lines and headers
Similar locking patterns are also found in posted tables, but there are some conceptual differences in the case of posted tables. In case of posted tables, the posted headers are locked before posted lines, and purchase tables are locked before sales tables. This is the critical point: sales tables have a lower locking privilege in comparison to purchase tables. Also, posted tables are locked before non-posted tables. In most cases, we can deal with posted sales and purchase tables separately, but when we need to use them by the same logic, then we must make sure all these locking patterns are followed.
Ledger entries and registers
In Dynamics NAV, it is important that a ledger entry table is always locked before its analogous register table. This is true for all the ledgers and its corresponding registers.
Most of the programmers make the mistake when they lock the table before carrying any activity which touches the locking pattern, that deal with the database activities.
Naming conventions in C/AL
A naming convention is nothing but a set of rules for adopting the character sequence to be used as an identifier, which includes variables, types, functions, and so on. These are a precise and consistent phrasing, which help the programmer better regulate the programs so that they can be properly understood by broader professionals. These abbreviating objects also help developers to understand the Cronus system internally before he or she can start customizing or applying some changes for the fulfillment of a requirement. This also helps in developing the features faster. The main difference it makes is when we need to review or make some changes to our code after some years-if we maintained some convention, then it would be easy to understand, whereas the lack of any pattern of a naming convention would make the whole process a cumbersome task.
Note
The compiler does not give a warning when using a local variable with the same name as the global variable. The system will always use the local variable first.
In Microsoft Dynamics NAV, we have been recommended several guidelines for naming objects, captions, local and global variables, functions, constants, and so on. I believe the developer can use any naming convention he or she likes. I do not want to point out the rules that one must follow, and at the end of the day, nobody really cares about the rules if the code works properly. The main concern should be defining descriptive names of the objects and variables. In some of the worst cases, I have seen crap like SL3 for Sales Line; this is not a standard by any means. In Microsoft Dynamics NAV 2016, the system generates the record type variable name automatically when you enter the name or number of the object in the variable page. It can be taken as the reference point, and you can see how the variables should be defined with simple descriptive names such as SalesLine for Sales line record. You can still append prefixes such as lSalesline for the local variable of sales line and gSalesLine for the global one. It all depends on what makes your program better and easier to understand so that the next time you or your teammates see your code, you are able to understand it easily.
Tip
I highly recommend using suffixes such as temp for record variables of temporary tables. This really makes sense when you are deleting the record from the temporary variable.