Detecting current object type and ID using some funky .NET Interop

Did you ever need to identify the current object type and ID, programmatically, from within the object? For example, detecting the current table ID in a table trigger like this guy? Or current codeunit ID from inside the codeunit?

Why would you need something like this? If you are inside a trigger in, say, table 18, you do know that you are in the table 18, and you can refer to it as 18 or DATABASE::Customer, right? Yes, but this is hardcoding. If you move this code to a different table you’d have to change the hardcoded constant to whatever that other table is.

Microsoft was well aware of the need to know the currently running object ID in some cases, because there is the OBJECTID function to the CurrPage and CurrReport built-in objects. However, for tables, codeunits, XMLports, and queries, there is nothing of the sort.

Now, using .NET Interop, you can easily (well, easy is relative) get this info.

Let me start with theory.

.NET provides a lot of functionality that gives insight into the internals of the .NET framework or current execution context. Namespaces such as System.Reflection, System.Threading, or System.Diagnostics are examples of it. In C# you can easily get access to the current method, or the method that called the current method. Also, you can detect the type of the currently executing class.

When you compile an NAV object, the C/AL compiler translates it into C#. The resulting C# file contains one class that represents the object. This means that any .NET Interop code you write within C/AL can gain access to the metadata of the currently running class.

So how do we gain access to the metadata of the currently running code? By using the stack trace. You can find this in the System.Diagnostics.StackTrace class.

A stack trace in .NET consists of frames, each frame representing a single execution context within the call stack. The first frame in the trace (index is 0) is the currently executing method context, the next frame is the context of the method that called it, and so on.

Let’s get practical now.

What I want to do is to have a codeunit – let me call it Current Object Management – and I want it to have these public functions:

SNAGHTML72e6d25

Then, from within anywhere – for example, from the Customer table – I want the CurrentObject.Type to return ‘Record’ and CurrentObject.ID to return 18. Let’s imagine that both the Type and ID functions call a local function called Detect that does the actual detection of the current object type through the stack trace. This would mean that the call stack would be the following:

image

However, if you create an instance of the StackTrace class with C/AL, and then iterate through the frames, you’ll quickly notice that the call stack is much more complex than what you’d expect it all to be. There are many helper classes and wrapper classes that are beefing up the call stack so it contains a lot more information than you need. This means that from within the CurrentObject.Detect method, the class to which this method belongs is not necessarily the codeunit itself. There is lot of wrapping and reflection in C# representation of C/AL code, and when you call the Detect method, it’s not called directly, but through the Invoke method of the MethodHandle class. The actual NAV context, in this case this Current Object Manager codeunit, is much deeper in the call stack. This makes things complicated.

To make it simpler, let’s take a look at what C# code for NAV object looks like. For example, when you create codeunit with the ID 50001, you’ll get a class named Codeunit50001 that inherits from the Microsoft.Dynamics.Nav.Runtime.NavCodeunit base class. This means that when looking through the call stack, we would need to look for the first frame where the declaring type inherits from the NavCodeunit class. This would represent the Current Object Management codeunit, and to find the caller object (which in fact is the current object, or in our example the Customer table) we need to dig further. However, the Customer table, which is Table18 in C#, does not inherit from NavCodeunit, but from the NavRecord type, and each different object has a different base class.

Thankfully, all NAV objects inherit from the Microsoft.Dynamics.Nav.Runtime.NavApplicationObjectBase class, and whenever digging through the stack trace, you only need to take those frames into account where the type descends from the NavApplicationObjectBase class.

So, the algorithm should follow this logic:

  1. Detect the first type that descends from the NavApplicationObjectBase class – that’s the Current Object Management codeunit
  2. Iterate through the call stack until you detect the next type that descends from the NavApplicationObjectBase class, which is different than the type detected in the first step – that’s whichever object called the Current Object Management codeunit.
  3. Stop iteration in step 2 if there are no more NavApplicationObjectBase descendants in the stack.
  4. Use a regular expression to parse the name of the type detected in the step 2 and get the object type and ID from it – this would get Record and 18 from Record18 type name.

Let’s start with this:

image

Here we get the System.Type instance for the NavApplicationObjectBase type, and then we iterate through the call stack to fetch the first type that descends from NavApplicationObjectBase. The LastFrame is an integer that represents the last frame ID through which we iterated.

The FindNextNavObject function looks like this:

image

It detects the current frame, then takes it’s method information, and then the information about the type to which the method belongs. Then it increases the frame counter. Then it makes sure to stop if the frame counter exceeds available frames, and also if the detected type is an indirect descendant of the NavApplicationObjectBase type (remember that each NAV object inherits from the object base type, and then from this type, for example Record18 : NavRecord : NavApplicationObjectBase). Finally, if we have iterated through all frames in the call stack, we clear the ThisType information.

Finally, to complete the detection of the calling class, write this:

image

It detects the next NAV object, and then continues is we are still within the Current Object Management codeunit. Finally, if we have found a type (which is always except when this codeunit is run directly) we parse the type using regular expressions.

This is how to do it.

image

The regular expression has two groups, one that matches one or more non-digit characters, and another that matches one or more digit characters. Since all NAV object types follow the same pattern, there is no error handling or unnecessary checks within this function – it simply assumes that the first group in the match represents the object type, and that the second group represents the object ID.

The remainder of the codeunit looks like this:

image

Finally, to test this, create a codeunit that calls the Current Object Management:

SNAGHTML74c2f3f

When you run it, it shows this:

image

There are many use cases for this functionality and the better structured code you write, the more you’ll need this information. For example, can you already see how this simple codeunit can help you simplify creating a framework for configuring parameters in a façade pattern based application?

To download the objects, follow this link: Currrent_Object_Management.zip

Vjeko

Vjeko has been writing code for living since 1995, and he has shared his knowledge and experience in presentations, articles, blogs, and elsewhere since 2002. Hopelessly curious, passionate about technology, avid language learner no matter human or computer.

This Post Has 5 Comments

  1. Kine

    What about returning the instance of the object? Like “This” to be able to pass This to another object…(e.g. like passing the actual posting codeunit instance to another object to be able to post something in the context of currrent posting, it means through the same instance of the posting codeunit…)

    1. Vjeko

      Kine: in theory, this should be possible. At least if it was pure C# and .NET world, it would absolutely be possible. However, since this is the whole NAV Application Framework (or whatever they officially call it) it requires pulling some tricks to get that done. I was actually exploring something of the sort and wanted to blog about that, and here you give me a perfect example: providing the “this” member that would give you the reference to itself. That would be mega-cool 🙂 Thanks for the idea!

      1. Kine

        You are welcome! Just keep the credits for the idea of “This” in C/Side to me… 😉

  2. Dennis van Es

    Vjeko,
    I know I’m late to the party, but what if I also need to know which record I originate from? As in I traced the call stack back to record 27, but I need to know the primary key or the record-id or something so I can retrieve that record.

    To clarify, I need to check things when a record is deleted, and since I’m to lazy to write an event for all tables, I’m using the global database delete trigger. However, for example when I delete an item, it will first delete all the related stuff (translations etc.) and that where the Global OnBeforeOnDatabaseDelete trigger kicks in. But I need to intercept the deletion of the item itself (and prevent that from occuring until certain conditions are met). Now with your code I found that the originating call came from record 27, but I still dont know which record id.
    Any thoughts if that is even possible?

    1. Vjeko

      It would be difficult. As a possible approach off the top of my head, you could listen to global trigger events (e.g. OnDatabaseInsert) and from there write to a temp table which gets somehow reset at the end of each call stack (that’s probably the toughest part). Then you’d be able to check the content of this temp table at any given moment and see if you are in a call stack that also came from somewhere else.

Leave a Reply