If, like me, you regularly need to navigate to e.g. your Dynamics NAV program folder, you may want to consider setting up an environment variable containing the path in question. Be sure to keep its name nice and short.

Environment Variables

The variable in question can then be used from Windows Explorer, the File|Open and File|Save As common dialogs, the Start Menu’s Run dialog, and also in the Command Prompt.

To navigate from anywhere else to e.g. your Dynamics NAV program folder, simply type cd %n% (where n is the name you chose for your environment variable):

Command Prompt

Cascading Table Relations

March 13th, 2012

Today, I wanted to talk to you about a very common category of table relation mistakes.

An example

In the example below, the fictitious table 70000 Customer Something has a 1:n relationship with the Customer table. It could contain customer addresses, customer contacts or something like that; the most important thing about it is its table relation with the Customer table. The Code field that’s also part of its primary key serves as a differentiator, thus allowing more than one record per Customer.

Table 70000 Customer Something

Table 70000 Keys

As one might expect, the Customer No. field has a table relation to the Customer table:

Customer No. Properties

Now, let’s introduce a line table which is a child (i.e. has a 1:n relationship to) of our Customer Something table. It contains the same primary key fields as its parent, in addition to a Line No. field that, again, serves as a differentiator, allowing more than one Customer Something Line per Customer Something to be inserted. If you are familiar with the typical document/document line structure in NAV’s base application, none of this should come as an awfully big surprise.

Table 70001 Customer Something Line

The naive approach

Some people naively define the line table’s fields as follows - please note the Customer No. field’s TableRelation property.

  FIELDS
  {
    { 1   ;   ;Customer No.        ;Code20        ;TableRelation=Customer;
                                                   NotBlank=Yes }
    { 2   ;   ;Something Code      ;Code10        ;TableRelation="Customer Something".Code WHERE (Customer No.=FIELD(Customer No.));
                                                   NotBlank=Yes }
    { 3   ;   ;Line No.            ;Integer        }
  }

Before you read on, think to yourself - would I have done this differently?

The correct approach (I think)

If you think about the TableRelation as defining a user’s potential choice of values, the logic above is obviously wrong: as the Customer Something Line’s customer number, you cannot pick just any old number from the Customer table: only the customers that are referenced from Customer Something records are candidates for selection in your Customer Something Line.

Theoretically true, without a doubt, but does it *really* matter, you might ask. Well, it does, when we start to rename some of our records. Let’s use the sample data below to illustrate this. As you can see, each of the two Customer Something records has a corresponding record in the Customer Something Line table.

Sample Data Before Rename

After renaming our Customer Something for customer 10000, we can see that the corresponding line wasn’t renamed, and is now effectively orphaned.

Sample Data After Rename - Wrong

Let’s change our TableRelation in the Customer Something Line’s Customer No. field as follows. Note that it now points to the Customer No. field in Customer Something which means users cannot select a customer no. that doesn’t already exist in Customer Something.

TableRelation - Right

Restoring the original sample data, and renaming it again, we get this:

Sample Data After Rename - Right

No rocket science, you say? I couldn’t agree more, but if I had a penny for every time I have seen somebody make this mistake, I would now have uhhh… a lot of pennies. :)

Randomize

February 24th, 2012

What do you think - does the code below produce the same random number every time?

And what about this code?

RecordRef.MODIFYALL?

February 23rd, 2012

Does anybody understand why RecordRef variables do not have a MODIFYALL method (when they do have a DELETEALL method)?

The other day, I fired up my Role Tailored Client (RTC) after having added some menu-items in our add-on’s menusuite (51) and its RTC counterpart (1051). Much to my surprise, the newly created items were visible in the Classic Client (CC), but wouldn’t appear in the RTC, no matter how many time I restarted the program (still my favourite and most effective remedy for RTC failures). A helpful colleague advised me to recompile all RTC-related menusuite objects (ID=1000..), and - lo and behold - my menu-items! :)

Function parameters of type Form

February 20th, 2012

I may be overlooking something (it is Monday morning, after all), but…

Form is a valid datatype for function parameters, but calling a function with a form-type parameter seems to always result in an error message. Am I doing something wrong here?

Function Parameter of Type Form not Allowed

Expected realisation in update - OnValidate()

The last post in this category for now; I think you’ll find this one a tad bit more challenging than its predecessors.

Please ignore the Dutch/Dunglish field names; I took this snippet from an internal application, which is why linguistic matters were not necessarily among the developer’s top priorities.

StartProcess function

If my job were to rewrite the function above, there are two things that I would definitely do differently. One is just a matter of convention, the other would strongly improve the readability (=maintainability) of the code.

Shouldn’t be too hard to spot. :)

The API Mindset (Part Three)

February 6th, 2012

In the last part of this series, I wanted to discuss what makes your API easy to call. In earlier posts, we’ve already covered things like naming of parameters and return values, variable scope and Command Query Separation. But there’s more you can do to make things easy for code calling your functions.

Dependency injection

First, let me stress that I’m probably not using this term (from the realm of software testability, AFAIK) correctly. What I mean is this: if a function does its job on an entity defined within that function (e.g. a record variable), that strongly limits what callers can do - they simply have no influence on the function’s subject. If that is the result of a conscious, justifiable, intelligent code design decision - that’s great. If it’s not, it may force your successors to copy your code instead of reusing it.

A typical (and common) example in C/SIDE is a function that performs a certain task on each record in a dataset (a poor man’s Visitor pattern?). If the record variable in question is defined as a local variable within that function (or worse - as a global variable of the object in which the function resides), it won’t be easy to let the function do its job on another dataset (than the one stored in the database table) - the most common example of which being a temporary record variable. A bit like this:

PROCEDURE LessFlexible()
VAR
  MyRecordVariable: Record;
BEGIN
  IF MyRecordVariable.FINDSET THEN
    REPEAT
      DoSomething(MyRecordVariable);
    UNTIL MyRecordVariable.NEXT = 0;
END;

vs.

PROCEDURE MoreFlexible(VAR MyRecordVariable : Record)
BEGIN
  IF MyRecordVariable.FINDSET THEN
    REPEAT
      DoSomething(MyRecordVariable);
    UNTIL MyRecordVariable.NEXT = 0;
END;

I guess it is a trade-off between flexility and effort required to call a function: the LessFlexible variant is easier to call, because the calling code does not require a record variable to pass as a parameter.

Complex parameters vs. simple parameters

Another related dilemma is the following: if my function does its job based on a number of field values in, say, a record variable, do I design the interface so that I need to pass the entire record variable to my function, or in such a way that I pass it only the field values it needs in its current implementation. Something like this:

VAR MyRecordVariable: record;

BEGIN
  […]
  SimpleValues(
    MyRecordVariable.MyFirstField,
    MyRecordVariable.MySecondField,
    MyRecordVariable.MyThirdField);
  […]
END;

PROCEDURE SimpleValues(MyParameter1: decimal, MyParameter2: decimal, MyParameter3: decimal) : decimal
BEGIN
  EXIT(
    MyParameter1 +
    MyParameter2 +
    MyParameter3); // e.g.
END;

vs.

VAR MyRecordVariable: record;

BEGIN
  […]
  EntireRecord(MyRecordVariable);
  […]
END;

PROCEDURE EntireRecord(MyRecordParameter: record)
BEGIN
  EXIT(
    MyRecordParameter.MyFirstField +
    MyRecordParameter.MySecondField +
    MyRecordParameter.MyThirdField); // e.g.
END;

The first option feels nicely decoupled, i.e. function SimpleValues doesn’t care at all where it’s input comes from, as long as it has the right datatypes. Function EntireRecord, however, assumes that its input takes the form of a record variable. If the calling code doesn’t have a record with the desired field values available, it’s going to have to prepare one in order to call EntireRecord.

On the other hand, EntireRecord is less likely to be impacted by changed requirements. If future versions of the algorithm involve field MyFourthField, EntireRecord already has access to the field, without changing the function’s parameter signature. SimpleValues would need an extra parameter, thus breaking any code that depends on it.

How do you normally cope with this devilish ;-) dilemma?

Picture3

Picture4

Picture6

Above, you see three object names. The last two suffer from the same problem. Strictly speaking, I think the first one is wrong suboptimal ;-p for two reasons. Can you see which?

Text Constants for XML creation

The text constants in the image above are used to import or export an XML file.

Please note that the opening and closing brackets (< and >) were introduced by C/SIDE (to indicate that the text constant values are entered in another language, and no corresponding values exist in the currently selected application language). In other words: the actual text constant values are “xml”, “version=’1.0′ encoding=’ISO-8859-1′” etc.

Tabs 'General', 'Data', 'Other' and 'Totals'

The form in the image above violates a best practice in C/SIDE-development (or in software development in general). Can you see which practice I’m talking about? :)

.NET Interop in NAV2009 R2

February 2nd, 2012

In this forum message, I wondered why I couldn’t instantiate a System.Xml.Linq.XElement object. The constructor expected an System.Xml.Linq.XName as its parameter, but it seemed XName did not have an appropriate constructor for me to call.

It turns out that I should have studied the documentation a bit harder - there’s a static factory method Get that can provide you with a shiny new XName instance.

My code now creates the XName, based on the local name and the namespace URL. This XName is then passed to XElement’s constructor. The next step would be to add the newly created XElement to an XDocument instance. XDocument has a parameterless constructor, as well as one that accepts an array of objects (System.Object) that represents the document’s content. That’s why I add my XElement to an ArrayList first in the example below.

XDocument Demo Code

And this is the result:

XDocument Demo Result

The API Mindset (Part Two)

January 31st, 2012

In the first part of this post, I explained why I believe the features of a C/SIDE application should be written with (what I call) the API mindset. Good APIs are designed to be consistent, intuitive and easy to call - making your successors more productive by allowing them to build on the solid foundation that you created. The guidelines below come from my own mental toolbox; some of them are inspired by somebody else’s wisdom (e.g. Robert C. Martin’s book Clean Code - warmly recommended!); others stem from my own hard-earned experience.

Consistent

In the NAV world, consumers of your code will usually start with virtually no knowledge about its inner workings. What they need to know (e.g. which function to call when), they will learn by looking at your function names, the way your functions are grouped, the types of values they accept as parameters, etc. That’s why consistency is key. If your function names follow a certain pattern, any interruption will stand out like a sore thumb. That’s perfectly OK if a function does not belong the “family” of functions preceding it. In all other cases, I would probably rename the function to make its name match its siblings’ names.

If several functions operate on the same set of parameters, be sure to keep the names, types and order of your parameters the same for each function. It will help flatten the learning curve for consumers of your code. Well-chosen parameter names (remember the GetFileName example from Part One?) are also useful in this area. The Record.COPY C/AL function springs to mind: I can never remember whether to pass the source or target record variable as a parameter. Parameter names to the rescue:

Record.COPY(FromRecord [, ShareTable])

Here’s another convention that I tend to follow. Sometimes you want your function library to “silently” report error conditions to you (using the return value), other times it makes more sense to let the library code raise a run-time error. Compare the following example from the .NET world: the Int32 structure in .NET defines two (overloaded) static methods for parsing a string to an Int32:

int Parse(string s) parses string s to an integer. In case of failure, it throws an exception - the equivalent of a C/SIDE run-time error.

bool TryParse(string s, out int result) parses string s to an integer. In case of success, the out parameter result contains the resulting integer value, and the function returns true. In case of failure, the function returns false, but doesn’t throw an exception.

The equivalent C/AL functions may look like this pseudo code, using the same Try prefix:

PROCEDURE Parse(s: string) result : integer
BEGIN
  IF NOT TryParse(s, result) THEN
    ERROR({ErrorMessageGoesHere});
END;

PROCEDURE TryParse(s: string; VAR result: integer) : boolean
BEGIN
  IF {CouldNotParseTheString} THEN
    EXIT(FALSE);

  result := {ParsedString};
  EXIT(TRUE);
END;

Now your callers can decide whether they want to handle the error condition silently (TryParse) or let you raise the error (Parse). And no, VAR parameters normally do not contribute much to an intuitive piece of code. ;)

Intuitive

As you probably know, I’m a big fan of limiting the scope of variables - in other words, to use locals whenever possible. That way, it is instantly clear what values a function may operate on: the ones passed in as parameters. There can be no side-effects on other variables, since the called function’s code cannot even “see” them.

To further secure and simplify the matter, Robert C. Martin suggests the principle of Command Query Separation: a function should either do something, or answer something, but not both. If a function’s purpose is to retrieve information, it shouldn’t change a few variables in the process. Although I agree with the general principle here, I can think of a few functions in the NoSeriesMgt codeunit that violate it, and still make perfect sense. Retrieving the next available number in a number series will, by its very definition, both answer something (”what is the next available number in this series”) and change something (i.c. the No. Series Line record). This kind of “retrieve and increment” style functions may be the exception to the highly recommended rule.

Apologies for the (unintended) cliff-hanger, but I’m just going to hit Publish and hope for your feedback on the above. A third part of this post will (finally) be dedicated to the “dependency injection” dilemma that I mentioned earlier. Pinky promise!

Normally, I try to write the minimal amount of code: e.g. I don’t think you’ll ever see me write NEXT(1) when a simple call to NEXT suffices. In some cases, however, I do write INSERT(FALSE), MODIFY(FALSE) or DELETE(FALSE) in order to show future maintainers of my code that I explicitly chose not to call OnInsert, OnModify or OnDelete (instead of having forgotten to even consider calling the trigger in question).

Makes sense? :)

The API Mindset (Part One)

January 17th, 2012

Even if that new feature you are working on doesn’t touch the sales order functionality, or doesn’t change any posting routines, it probably has some dependencies on application development work that was done earlier - whether by the application developers at Microsoft, your co-workers, or perhaps even yourself. Pretty much all development in C/SIDE has existing application objects as its basis - if you need something like number series, it’s quite uncommon not to use what’s already there in the base application. If the Excel Buffer meets your needs, it would be silly to write something similar yourself. In my view, that implies that all C/AL code should be written as an API, an application programming interface; i.e. with future consumers of the interface you provide in mind. A number of requirements instantly follow from this assumption - let’s see how they apply to C/SIDE.

Well-defined, well-documented

A good requirement to start with, since it appears to be so poorly executed in the base application. Take codeunit NoSeriesManagement as an example. It’s something that (for reasons of consistency) we’re all supposed to use when dreaming up new master and document entity types. Nonetheless, looking at the (non-local) functions in codeunit 396, it is not instantly clear what each one does, despite their consistently applied naming. I’m pretty sure the same is true for certain methods in the .NET framework, but in that case, there’s plenty of formal documentation and examples on the MSDN site. A tough problem to resolve, and likely to remain that way for a long time to come. Perhaps a move to C# could facilitate and standardize the way we generate interface documentation? ;-)
Carefully selecting the right names for your artefacts will provide at least some documentation for consumers of your API. The necessity of descriptive function names has been the subject of some earlier post; today, let’s focus on return values. Compare the following signatures for a function that retrieves the file name (e.g. “bar.txt”) from a file path (e.g. “c:\foo\baz\bar.txt”).

PROCEDURE GetFileName(Value: Text[260]): Text[260]
PROCEDURE GetFileName(FullPath: Text[260]): FileName : Text[260]

If you had asked me a few years ago, I would have strongly advised against named return values unless strictly necessary. These days, however, I always consider naming my return values (even if they are never explicitly assigned to), just to provide users of my code with that extra bit of information about exactly what is being returned; even more so if the returned value isn’t as instantly clear from the function’s name as it is in the example above.

Minimal, hiding implementation

At the risk of being accused of riding my hobby horse (again!): I think it is essential to minimize your interface. Only functions that you explicitly expose as your API should be visible to the outside world. Anything else (and that typically consists of implementation details) should have its Local property set to Yes. Not only does this make the interface smaller, and therefore easier to understand, it also allows you to change the implementation later without affecting the callers of your code. If you “publish” a method (i.e. leaving Local set to No), don’t blame your successors for calling it, effectively blocking you from changing something without breaking backward compatibility.

And yes, I agree, the designers of the C/SIDE platform probably should have made Local=Yes the default state, much like in C#, where class members without a visibility specifier are considered private. Now that they haven’t done so, there’s no way back: e.g. the text format in which objects can be exported is structured in such a way that absent information is assumed to have it’s default value. Interpretation of the absence of the keyword LOCAL cannot be changed without breaking backward compatibility.

Generic helper functions are an interesting exception, it seems. Suppose you’re writing a codeunit to implement some high-level functionality. Some of your functions may act as helpers to other functions (functions for string manipulation, file name manipulation etc. spring to mind) within your codeunit. In the context of your codeunit, they are implementation details, but unlike many others, they could prove useful outside the scope of your codeunit. Leaving them as global (Local=No) functions in your codeunit, however, makes them part of the interface of your feature. Customers that didn’t purchase the feature’s granule will not have access. Ergo: resist the temptation to make these helper functions global. If they are as generic as you think they are, move them into a separate codeunit in the base granule of your application.

To be continued. In the next part, I hope to cover dependency injection and loose coupling (probably neither in the strictest sense of the word).

Earlier today, my attempts to import a translation file (in text format; not a language module) were met with this error message - equally persistent as it was uninformative:

Microsoft Dynamics NAV Classic has stopped working. Windows is checking for a solution to the problem.

which, after a while, was replaced with this:

A problem caused the program to stop working correctly. Windows will close the program and notify you if a solution is available.

Luckily, the error message turned out to be overly drastic, and the client was still running after I clicked Close program. It took me some time to figure out what had caused this problem. In the end, I was able to reproduce the problem with a tiny two-line translation file:

My translation file

As you can see, the file erroneously contains the same “signature” (T11024193-F6-P8629-A1043-L30) twice. When editing the file before, I had forgotten to change the language code in the second line. Be sure to check for lines like these if you can’t seem to import a translation file! :)

conv-ml

December 20th, 2011

The last few days, I’ve been working on something that was long overdue: making our solution fully multi-language (ML) aware. Until now, there has not been much use in doing so, since we cater exclusively to Dutch-speaking customers. As we are preparing for the Certified for Microsoft Dynamics certification, however, ML-readiness has become more important.

My first step was to convert (relevant - see below) hard-coded text in the C/AL code to text constants, using conv-ml. Below, you’ll find my experiences.

First of all, I needed to find the ML conversion tools. Luckily, the NAVW14.00 UPGTK is available for download from www.mibuso.com, saving me some time that would otherwise have been spent looking for old product CDs. Please note that conv-ml contains 16-bit components. Windows 7 does not support these, but does come with a VirtualPC-based solution for running in Windows XP compatibility mode.

The syntax for running conv-ml is quite straight-forward:

conv-ml {source.txt} {target.txt} {translation.txt} {idoffset} {languagecode} {languageid}
  • {source.txt} is a text export file containing the source objects;
  • conv-ml writes its output to {target.txt};
  • {translation.txt} should contain a translation export of the source objects;
  • created text constants are named “Text”, followed by the first available ID greater than {idoffset};
  • hard-coded text currently in the objects is assumed to be in the language represented by {languagecode} (3-letter code) and {languageid} (numerical code); only text constant values for this language are created.

In my case, the command line read:

conv-ml source.txt target.txt translation.txt 11024000 NLD 1043

Make sure the old hard-coded text is in fact in the language specified in {languagecode}/{languageid}, or you’ll end up with very confusing results.

Thanks to {translation.txt}, conv-ml knows which text constant names already exist, and will avoid naming conflicts. Generated text constants do not yet have a UID (Unique ID). They will not be assigned until the objects from {target.txt} are imported into a database and compiled.

It’s important not to create too many text constants - not every bit of hard-coded text is a candidate for text constant conversion.

  • In our solution, date formula constants (e.g. ‘<CM+1D>’) form the largest category of falsely converted hard-coded text. I’m reverting them to their previous form (making sure they are properly ML-aware by surrounding them with “<” and “>”), and deleting the corresponding text constants. There must be a cleverer way to do this…
  • File format specific information, such as XML element names in file exports, are non-translatable either and should remain hard-coded.
  • Finally, please note that conv-ml is quite eager to convert strings - commented out hard-coded text or even text between single quotes in the Documentation section is converted, and should be reverted manually.

Please be so kind as to share your own conv-ml experiences in the Comments section! :)

Although I left out some less relevant stuff here and there, this blog post is actually based on a true story. It is meant to illustrate how rethinking the structure of an application can improve its usability, maintainability and flexibility – all at the same time.

Imagine a simple document-style entity type. It has a No. (probably based on a No. Series) as its primary key, a Description field and a Status field.

Initial situation

Pretty soon, it is decided that the Document table should also keep track of which developer is associated with each document.

Developer field gets added

Instead of having developers periodically check for changes in the Status field themselves, it is then decided that they should receive an e-mail whenever the field’s value changes. Although the developer’s e-mail address is stored elsewhere, it is displayed on the document card.

E-Mail Address gets added

Pretty soon after that, another role is introduced. Obviously, this person should also be informed when the Status changes.

Project Manager field gets added

This is when the initial design is starting to come apart at the seams:

  • There are plans for adding even more roles, compromising the usability of the form
  • People who are not formally involved in the project would like to be notified, too.
  • Some people who are formally involved are getting tired of being spammed with status updates.

To deal with this problem (which, in reality, was a bit more complex than in this example…) we introduced the concept of subscribers: a list of people (for each document) that wish to be kept "in the loop".

Subscribers table design

By default, anybody in a formal role (i.c. the developer and project manager) gets added to this table, but can be removed. Interested third parties can easily be added as required.

Subscribers in action

Please let me know what you think about this approach!

P.S.: As usual, I may have cut some corners for simplicity’s sake (e.g. if the value in the developer field is changed, should the system remove the old developer from the subscribers table?). If you see more serious flaws, I’d be more than interested to hear from you!

FOLLOW-UP

A little follow-up on the post above, in response to some feedback that I received from Luc van Vugt (http://dynamicsuser.net/blogs/vanvugt/default.aspx). Thanks, Luc!

As I said, at the highest level, my post was meant to illustrate how simple changes in an application’s structure can bring significant improvements. At a slightly more detailed level, it illustrates a pattern that is not uncommon in steadily growing applications.

In the example above, similar groups of fields (i.c. User ID, Name and E-Mail fields) are added to an existing application. The presence of such groups should trigger us to consider “tilting” the data model: converting each group into a record in a new table. This way, you’re turning “horizontal growth” (more fields; more code; more complexity) into “vertical growth” (more records, same code, same complexity). Makes sense?

In a C/SIDE application (whose true identity shall remain unknown, to protect both the innocent and my job security ;)), I recently read this function in table 5050 Contact:

CreatePerson()
GetRMSetup;

locContact := Rec;

INIT;
“No.” := NoSeriesMgt.GetNextNo(RMSetup.”Contact Nos.”,TODAY,TRUE);
Type := Type::Person;
INSERT(TRUE);

IF locContact.Type = locContact.Type::Company THEN BEGIN
  COMMIT;

  CLEAR(locRole);

  IF FORM.RUNMODAL(FORM::Roles,locRole) = ACTION::LookupOK THEN BEGIN
    locContactRole.INIT;
    locContactRole.”Contact No.” := “No.”;
    locContactRole.VALIDATE(”Role Code”,locRole.Code);
    locContactRole.”Related Contact No.” := locContact.”No.”;
    IF locContactRole.INSERT(TRUE) THEN;
  END;
END;

It contains a few patterns that I usually try to avoid. As you can see, the function contains a COMMIT statement, probably to allow the RUNMODAL following it. Furthermore, being located in a table, it work directly on Rec. The function duplicates some code present elsewhere in the system, such as the code for retrieving a number for the newly created contact.

My version of the function looks something like this:

CreatePersonNew()
CreateRole := FORM.RUNMODAL(FORM::Roles, Role) = ACTION::LookupOK;

Contact.Type := Contact.Type::Person;
Contact.INSERT(TRUE);
Contact.VALIDATE(”Company No.”, “No.”);
Contact.MODIFY(TRUE);

IF CreateRole THEN BEGIN
  ContactRole.”Contact No.” := Contact.”No.”;
  ContactRole.VALIDATE(”Role Code”, Role.Code);
  ContactRole.”Related Contact No.” := “No.”;
  IF NOT ContactRole.INSERT(TRUE) THEN;
END;

As you can see, it’s shorter (more about this in a later blog post). The COMMIT was eliminated by getting the user input (FORM.RUNMODAL) before starting the write transaction (INSERT). It no longer does its job on Rec, but on an other variable instead - much safer. Finally, it relies on the Contact table’s OnInsert trigger to find the next available number in the number series.

Do you think my version is an improvement? Do you see flaws in my code? Please let me know! :)