Method as a parameter (in D365FO)

We usually have methods where the logic is written in code and it just receives some data in parameters. But sometimes it’s benefitial to allow the caller code to define a piece of logic by itself.

For example, I can write code to filter a collection and let consumers decide what condition should be used for the filter. This kind of things is heavily used in LINQ in .NET.

Or I may have a piece of code to select records from database and perform some actions on them, and while the selection logic is the same, the actions that callers may want to perform are unknown to me. Therefore I could create a method that fetches the data and then takes the actual logic as a parameter.

Let me demonstrate it on a simple example. I’ll have a collection of data (a Set object) and I want an easy way to run an action on each of the elements.

Therefore the iteration of elements will be done by my code, but the actual action will be provided as a method parameter.

To make the method easily discoverable, I’ll add it to the Set class as an extension:

[ExtensionOf(classStr(Set))]
public final class My_Set_Extension
{
    public void forEach(System.Delegate _action)
    {
        SetEnumerator enumerator = this.getEnumerator();
        while (enumerator.moveNext())
        {
            System.Object[] parameters = new System.Object[1]();
            parameters.SetValue(enumerator.current(), 0);
            _action.DynamicInvoke(parameters);
        }
    }
}

I’m using a .NET class called System.Delegate as the parameter, because X++ as such doesn’t have any appropriate type. It’s the beauty of the interoperability of X++ with .NET – we can benefit from the whole .NET platform.

The actual iteration of the collection is straightforward. To execute the provided method, we use DynamicInvoke(), which expects an object array with method parameters. We have a single parameter (the element of the collection), therefore we create an array of size 1, put the element there and pass the array to DynamicInvoke().

Before we call our forEach() method, let’s create a method preparing some test data for us:

Set getData()
{
    Set set = new Set(Types::String);
    set.add('Primus');
    set.add('Secundus');
    set.add('Tertius');
    return set;
}

And this will be a method that we want to call for each element:

void reverseAndShow(str _s)
{
    info(strReverse(_s));
}

Because we’ll iterate a collection of strings, the parameter type is str.

Now we need to define a delegate, i.e. the type for our method. The declaration of a delegate in X++ looks like a method declaration, but it’s a very different thing under the hood. We’ll basically get a variable, a type of which is a class (generated under the hood, inheriting from System.Delegate) that can hold references to methods with a particular list of parameters and a return type.

Our method accepts a string and returns void, therefore we need a delegate with the same profile.

delegate void actionDelegate(str _s)
{
}

Now it’s time to make the call:

void run()
{
    this.actionDelegate += eventhandler(this.reverseAndShow);
 
    Set set = this.getData();
    set.forEach(this.actionDelegate);
}

We use the newly created delegate and add the reverseAndShow() method to it by += eventhandler(…).

Then we get the Set with data, call its forEach() method and pass the delegate to it. If you remember, we declared the parameter of forEach() as System.Delegate, which is the parent of what we get from this.actionDelegate.

If you run it, you’ll see that it indeed iterates the Set and calls reverseAndShow() for each element.

A nice feature of delegates is that they can hold references to several methods at once, and invoking the delegate executes all the methods. Let’s add one more method to our solution:

void toUpperAndShow(str _s)
{
    info(strUpr(_s));
}

Then we’ll simply add one more delegate subscription to run() method – no other change is needed.

void run()
{
    this.actionDelegate += eventhandler(this.reverseAndShow);
    this.actionDelegate += eventhandler(this.toUpperAndShow);
 
    Set set = this.getData();
    set.forEach(this.actionDelegate);
}

Now both methods are executed for every element of the collection.

For your convinience, here is a complete class that you can copy and run.

internal final class DelegateDemo
{
    public static void main(Args _args)
    {
        new DelegateDemo().run();
    }
 
    void run()
    {
        this.actionDelegate += eventhandler(this.reverseAndShow);
        this.actionDelegate += eventhandler(this.toUpperAndShow);
 
        Set set = this.getData();
        set.forEach(this.actionDelegate);
    }
 
    Set getData()
    {
        Set set = new Set(Types::String);
        set.add('Primus');
        set.add('Secundus');
        set.add('Tertius');
        return set;
    }
 
    delegate void actionDelegate(str _s)
    {
    }
 
    void reverseAndShow(str _s)
    {
        info(strReverse(_s));
    }
 
    void toUpperAndShow(str _s)
    {
        info(strUpr(_s));
    }
}

Note that DynamicInvoice() is slower than direct method calls. It’s typically not a problem, but you need to keep it in mind if you’re writing performance-critical code that makes a lot of such calls.

The power of well-designed code

My previous blog post reminded me a refactoring that I did on one of my previous projects (in AX 2012).

I needed to adjust a piece of logic and found that it’s implemented as a method with more than two thousand lines. Of course, nobody could understand what’s going on there. Such code in unmaintainable.

After exploring it and comparing parts of the method, I realized that it consists of many almost identical blocks of code. There where select statements with slightly different conditions, otherwise the code was the same. The developer clearly didn’t know how to write the queries dynamically and also didn’t think about extracting the remaining code to a method to be called from each place. When he needed a new condition, he put the current method body to an if block, duplicated the whole method body in the else block and changed a few things in where clauses.

Because no one could understand the method, there were even two more copies of the whole method, when someone needed a slightly different behavior and didn’t dare to adjust this enormous method for another scenario.

The logic was somewhat similar to what I described in my previous blog post. We ran a specific query to find some data and tried to do something with it. If it didn’t give us the expected result, we tried a slightly more generic query and then even a more generic one, and so on. You can imagine it as finding a discount for a particular customer and item, then trying to find it for an item and a group of customers etc. (The real scenario isn’t important and I don’t even remember it well anymore.)

What I did was extracting the logic to a method that took a query as a parameter. Then I created a collection of Query objects and executed them one by one, always checking the result.

The queries were created by methods, each with a name describing the business scenario. Therefore the code creating the list of queries looked like this:

add(this.findByItemAndCustomer());
add(this.findByItemAndCustGroup());
add(this.findByItemOnly());
... approximately ten queries in total ...

Then I easily added my logic and I asked a business consultant to verify that we have the right set of rules and we execute them in the right order. Although he wasn’t familiar with X++, he was pleasantly suprised to learn that he actually could read the code and understand what it does – and therefore review whether it meets business requirements.

So… we got from a method that even no developer could make sense of, to something that even non-programmers can understand.

Dynamic list of methods to execute (in D365FO)

Imagine that you’re running a set of payment matching rules or you’re trying to find discounts based on various criteria.

You’ll try one way to find the data, if it doesn’t bring anything, you try another one, and so on.

A typical implementation looks like this:

found = trySomething();
 
if (!found)
{
    found = trySomethingElse();
}
 
if (!found)
{
    found = tryYetAnotherThing();
}

It’s straightforward and it works reasonable well as long as the list is static. But what if you need more flexibility? For example, maybe you want the caller code to decide which methods should be used (e.g. just those selected by a user) and in which order. Then this approach doesn’t really work.

A classic object-oriented solution would be creating a separate class for each method – each class would represent a single strategy for performing the task. These classes would implement a common interface, which would allow working with them in a polymorphic manner. Then you could easily create a collection of such classes and run them one by one.

This approach is fine and I would recommend it in more complex scenarios, but if the logic is simple, creating an extra class for each method may be cumbersome. Isn’t there any alternative?

A solution may be a collection of delegates. A delegate is basically a reference to a method (or more methods at once, because delegates can be combined together, but that’s not what we need today). In F&O, they’re typically used to allow extensions, but their potential usage is much broader.

Instead of creating a separate class for each method, we’ll create a single class representing a strategy to do something – in this case, to find a discount.

public final class DiscountFinderStrategy
{
    delegate void findDiscountDelegate(ItemId _itemId, DiscountFinderResult _result)
    {
    }
 
    public void execute(ItemId _itemId, DiscountFinderResult _result)
    {
        this.findDiscountDelegate(_itemId, _result);
    }
}

I thought that I could call the delegate directly from other classes, but it doesn’t work. That’s why I added execute() method, which does nothing but calling the delegate.

Here are three methods that we’ll want to use for the price search:

void findDiscountForItem(ItemId _itemId, DiscountFinderResult _result)
{
    info("Finding discount for specific item...");
    if (_itemId == 'aaa')
    {
        _result.parmFound(true);
        _result.parmDiscount(5);
    }
}
 
void findDiscountForItemGroup(ItemId _itemId, DiscountFinderResult _result)
{
    info("Finding discount for item group...");
}
 
void findDefaultDiscount(ItemId _itemId, DiscountFinderResult _result)
{
    info("Finding default discount...");
 
    if (System.DateTime::Now.DayOfWeek == System.DayOfWeek::Monday)
    {
        _result.parmFound(true);
        _result.parmDiscount(1);
    }
}

They don’t do anything real; their main purpose is adding messages to infolog, so we can see that they were called.

findDiscountForItem() returns a result for item aaa, therefore subsequent methods won’t be called. We’ll test it later.

The discount in findDefaultDiscount() is there just to make Mondays more bearable for you. 😉

Delegates in CLR can return values and if it was possible in F&O, I could return the result directly from each method. Unfortunately delegates in F&O allow only void return type, most likely because they were designed to be used as events in particular and not as general-purpose delegates. But it’s not a big deal – we can make a workaround by using an object as a parameter and set the values there. That’s the purpose of my DiscountFinderResult class.

public class DiscountFinderResult
{
    boolean found;
    real discount;
 
    public boolean parmFound(boolean _found = found)
    {
        found = _found;
        return found;
    }
 
    public real parmDiscount(real _discount = discount)
    {
        discount = _discount;
        return discount;
    }
}

To call my three methods, I’ll create three instances of DiscountFinderStrategy class and subscribe one method to each.

DiscountFinderStrategy findByItem = new DiscountFinderStrategy();
findByItem.findDiscountDelegate += eventhandler(this.findDiscountForItem);
 
DiscountFinderStrategy findByGroup = new DiscountFinderStrategy();
findByGroup.findDiscountDelegate += eventhandler(this.findDiscountForItemGroup);
 
DiscountFinderStrategy findDefault = new DiscountFinderStrategy();
findDefault.findDiscountDelegate += eventhandler(this.findDefaultDiscount);

By the way, I could add all three methods to a single delegate, but then all of them would execute (unless an exception is raised). That’s not what I want in this case. I want to run once, check the result, run the other one and so on.

Then I need something that will actually execute the methods. I’ve designed DiscountFinder class for this purpose:

public final class DiscountFinder
{
    List strategies = new List(Types::Class);
 
    public void addStrategy(DiscountFinderStrategy _strategy)
    {
        strategies.addEnd(_strategy);
    }
 
    public real findDiscount(str _itemId)
    {
        setPrefix(strFmt("Item %1", _itemId));
 
        DiscountFinderResult result = new DiscountFinderResult();
        ListEnumerator enumerator = strategies.getEnumerator();
        while (enumerator.moveNext())
        {
            DiscountFinderStrategy strategy = enumerator.current() as DiscountFinderStrategy;
            strategy.execute(_itemId, result);
 
            if (result.parmFound())
            {
                return result.parmDiscount();
            }
        }
 
        return 0;
    }
}

It holds an ordered collection of DiscountFinderStrategy objects in strategies variable. Caller code can add them by calling addStrategy() method. findDiscount() iterates the collection, calls execute() method (which runs the delegate and therefore the subscribed method) and checks the result. If a result was found, the execution ends and a discount is returned. If not, the next strategy to find a discount is used.

Finally, here is an example of the caller code, implemented as a runnable class:

internal final class DiscountFinderDemo
{
    public static void main(Args _args)
    {
        new DiscountFinderDemo().run();
    }
 
    void run()
    {
        setPrefix("Discount search");
 
        DiscountFinder discFinder = this.createDiscountFinder();
 
        discFinder.findDiscount('aaa');
        discFinder.findDiscount('bbb');
 
    }
 
    DiscountFinder createDiscountFinder()
    {
        DiscountFinder discFinder = new DiscountFinder();
 
        DiscountFinderStrategy findByItem = new DiscountFinderStrategy();
        findByItem.findDiscountDelegate += eventhandler(this.findDiscountForItem);
        discFinder.addStrategy(findByItem);
 
        DiscountFinderStrategy findByGroup = new DiscountFinderStrategy();
        findByGroup.findDiscountDelegate += eventhandler(this.findDiscountForItemGroup);
        discFinder.addStrategy(findByGroup);
 
        DiscountFinderStrategy findDefault = new DiscountFinderStrategy();
        findDefault.findDiscountDelegate += eventhandler(this.findDefaultDiscount);
        discFinder.addStrategy(findDefault);
 
        return discFinder;
    }
 
    // Here are the three find* methods that we saw earlier
 
    void findDiscountForItem(ItemId _itemId, DiscountFinderResult _result)
    {
        info("Finding discount for specific item...");
        if (_itemId == 'aaa')
        {
            _result.parmFound(true);
            _result.parmDiscount(5);
        }
    }
 
    void findDiscountForItemGroup(ItemId _itemId, DiscountFinderResult _result)
    {
        info("Finding discount for item group...");
    }
 
    void findDefaultDiscount(ItemId _itemId, DiscountFinderResult _result)
    {
        info("Finding default discount...");
 
        if (System.DateTime::Now.DayOfWeek == System.DayOfWeek::Monday)
        {
            _result.parmFound(true);
            _result.parmDiscount(1);
        }
    }
}

And this is the result:

Only one method was called for item aaa, because it managed to find a discount. On the other hand, we had to try all available strategies for item bbb.

This is just one example of how delegates can change the way how you design applications. I’m going to show another one soon.

Fixed-width text files in data management (F&O)

Data management in Dynamics 365 for Finance and Operations supports several types of files for import and export. I usually see XML, Excel and CSV used, but sometimes we also need to deal with a text format with fixed-width columns. It means that there is no column separator as in CSV – we know that first ten characters contains an ID, next eight characters contains a quantity or something like that.

Data management supports this scenario too, which not everyone is aware of.

To try it, first set up a source format. Go to Data management workspace, click Configure data source tile and create a new format. Set File format = Fixed width and possibly other parameters as needed. I’ll use First row header = No and Row delimiter = {LF}. It doesn’t seem to work very well with {CR}{LF}.

Then create an export project and add an entity to be exported with our new source format. I used Customer groups entity (because it’s relatively simple and it doesn’t contain too many records).

Still in the project, notice Entity attribute button. It’s very important when talking about fixed-width files.

If you press it, you’ll see a list of columns that will be in the file. There is a sequence of columns, their length etc., and you can modify it as needed.

If you want to import the file, create an import project and add an entity with the fixed-width source format. It’ll complain about missing mapping, unless the columns have headers and data management is able to map them to fields.

In this case, we’ll configure it manually. As we already know, the Entity attribute button will take us into the setup of file columns. Here we can add the columns, their type, lenght and so on.

Then we can use these columns in source mapping in the usual way.

By the way, when I needed to deal with fixed-width text files in C# (namely Azure Functions), I used FixedWidthParserWriter, which did the job perfectly.

Animated GIF thumbnails from a video

This blog post isn’t about Dynamics AX / Dynamics 365, although it may occasionally be useful for some AX/D365 developers as well.

Let’s say I have a 30 minutes long movie that I want to share on a website and I want to give people an idea about the content of the video. Therefore I want to create a slideshow of screenshots taken from the movie at regular intervals. An animated GIF is an obvious choice.

I spent quite some time trying to figure out how to do it, because I had little idea. After some research, I came to a conclusion that a reasonable approach is extracting screenshots with FFmpeg and then combining them to a GIF file with ImageMagick.

Because installing those console applications and dealing with them directly would be quite cumbersome, I also looked for some wrapper libraries available via NuGet. I chose these:

Now let’s build a simple application to show the code. Create a console application for .NET Framework and add those two NuGet packages.

Add using statements for namespaces that we’ll need in a moment:

using System.IO;
using ImageMagick;
using MediaToolkit;
using MediaToolkit.Model;
using MediaToolkit.Options;

The structure of your program will be following:

class Program
{
    static void Main(string[] args)
    {
        new Program().Run();
    }
 
    void Run()
    {
        string videoFilePath = @"c:\temp\input.mp4";
 
        List<string> thumbnails = ExtractThumbnails(videoFilePath, 10);
 
        if (thumbnails?.Any() == true)
        {
            string gifFilePath = Path.Combine(
                Path.GetDirectoryName(videoFilePath),
                $"{Path.GetFileName(videoFilePath)}.gif");
 
            CombineThumbnails(gifFilePath, thumbnails);
 
            // Delete temporary files
            thumbnails.ForEach(file => File.Delete(file));
        }
    }
}

ExtractThumbnails() takes screenshots from the video as JPG files.

CombineThumbnails() then take these files and put them into an animated GIF.

Finally we delete JPG files, because they aren’t needed anymore.

Here is the implementation of ExtractThumbnails(). The key part is the call of engine.GetThumbnail() at the end.

List<string> ExtractThumbnails(string inputFilePath, int numOfPics)
{
    MediaFile inputFile = new MediaFile { Filename = inputFilePath };
    List<string> thumbnails = new List<string>(numOfPics);
 
    using (var engine = new Engine())
    {
        engine.GetMetadata(inputFile);
        if (inputFile.Metadata == null)
        {
            throw new InvalidOperationException("Invalid file");
        }
 
        int duration = (int)inputFile.Metadata.Duration.TotalSeconds;
        int picDistance = duration / (numOfPics + 1);
 
        for (int i = 1; i <= numOfPics; i++)
        {
            var options = new ConversionOptions { Seek = TimeSpan.FromSeconds(i * picDistance) };
 
            string outputFilePath = $"{inputFilePath}{i}.jpg";
 
            var outputFile = new MediaFile { Filename = outputFilePath };
            engine.GetThumbnail(inputFile, outputFile, options);
            thumbnails.Add(outputFilePath);
        }
    }
 
    return thumbnails;
}

The last missing piece is the method creating the animated GIF. It’s straightforward, thanks to Magick.NET:

void CombineThumbnails(string gifFilePath, List<string> thumbnails)
{
    using (var collection = new MagickImageCollection())
    {
        for (int i = 0; i < thumbnails.Count; i++)
        {
            collection.Add(thumbnails[i]);
            collection[i].AnimationDelay = 70;
            collection[i].Resize(800, 600);
        }
 
        collection.Write(gifFilePath);
    }
}