Getting started with NAV and .Net Interop – Part 4

Once more into the breach

Jeez, Part 4 already?
Last time in Part 3 we imported files using streams and learned a bit more about .Net and our stamina to read boring code. The previous example was a tad bigger than the first one, but now we’ll wrap that into an even bigger example! Exampleception !!

Import all files in a folder

We have all done this before. When you want to import a file you make a Record variable on the File record. Filter it, loop over it, build your filename, and import it. Something like this:

lv_ImportPath := 'C:\Import'; //You get this from Setup or the user of course

//Filter
lt_Files.SETRANGE(Path,lv_ImportPath);
lt_Files.SETRANGE("Is a file",TRUE);
lt_Files.SETFILTER(Name,'*.csv');

//Loop
IF lt_Files.FINDSET THEN BEGIN
  REPEAT

    //Build file path and import file

  UNTIL lt_Files.NEXT = 0;
END;

I’ve always found this solution clunky when it works. Sometimes it won’t. We can do better right? RIGHT!

So we Google “c# read files in directory”:






































à
// Process all files in the directory passed in, recurse on any directories  
// that are found, and process the files they contain. 
public static void ProcessDirectory(string targetDirectory) 
{
    // Process the list of files found in the directory. 
    string[] fileEntries = Directory.GetFiles(targetDirectory, ".csv");
    foreach(string fileName in fileEntries)
        ProcessFile(fileName);
}

// Insert logic for processing found files here. 
public static void ProcessFile(string path)
{
    Console.WriteLine("Processed file '{0}'.", path);       
}

So what’s happening here?
-          Using the Directory object, use GetFiles to get an Array of filenames
-          Loop over the filenames with an Enumerating-loop
-          Process them (in our case import them)

With some rewriting and some additional Googling we can come to the following DotNet-powered C/AL:

ldn_Filenames := ldn_Directory.GetFiles(lv_FilePath, '*.csv');
ldn_Enumerator := ldn_Filenames.GetEnumerator();
WHILE ldn_Enumerator.MoveNext() DO BEGIN;
  lv_FileName := ldn_Enumerator.Current;
  //Import file
END;

A soapbox about Enumerators

You see me using an Enumerator there to loop through the Array. “But Vincent” you’ll say “why not define an int-variable and do a FOR-loop until the Array’s Length property minus 1. Wouldn’t that be more like the C# example?”. And I’ll say “No”. Because the above C/AL code describes what the foreach in the C# loop does behind the scenes:
-          It gets an Enumerator from the collection
-          It asks the enumerator to fetch the next item
-          If the enumerator comes up blank, the loop is done

The big difference here is:

The loop does not need to know how many items there are in the collection

I used the “emphasis” style for that! Why? Because it’s important: If the loop does not care how many elements there are in the collection it doesn’t need a count. Because some collections only know their count by, sometimes painstakingly slow, enumerating over all their elements. Sometimes collections can only be enumerated once and they’re gone. So an enumerator goes for the simplest loop-construction: start the loop, get each item, and end the loop.

This is a pattern used throughout .Net programming: If you can help it, don’t enumerate collections twice. The code execution is often optimized to quickly find the next item from the previous one and finding any “random” item from an index might actually be very slow. So unless you know damn sure the above isn’t true in your case or you don’t care about speed and efficiency (GASP!).

So yes, another contrived example intended to teach you about programming things DotNetStyle.

Where do I come up with it?

I have no clue what part 5 will be about.

Reacties

Populaire posts van deze blog

Getting started with NAV and .Net Interop – Part 3

Getting started with NAV and .Net Interop – Part 1