A few years back I faced a couple of elusive issues that caused me many a sleepless night. I called them the “downward spiral into madness” issues. They prompted me to write a guidelines document to assist my fellow Dynamics comrades in avoiding the same pitfalls as I.

The gates to hell

The issue

We often follow our instinct to program plugins just like any other straightforward application. Unfortunately, the plugin pipeline architecture is a little different than what we would expect.

Of notable mention is how Microsoft, in an attempt to improve performance and reduce memory usage, caches plugin instances and reuses them for all triggered executions of the same type. The IPlugin object is instantiated once, and then the Execute method is passed a special IServiceProvider object for every execution.

The following snippet shows the issue:

Copy to Clipboard

This fact leads us to conclude that a plugin object must be treated as a static class, in fact. One might ask: what’s the harm in actually treating the object as a static class? When dealing with shared data or objects, we must take into consideration thread safety.

Objects you control fall under your responsibility; so it’s relatively easy to predict their behaviour under threaded access. However, Dynamics objects are not so easy to predict. IOrganisationService objects are tied to a transaction in the DB. Using the object outside of said transaction causes so many unpredictable issues: data saved in the wrong record, strange transaction-related errors, … etc.

To add salt to the wound, most of those issues pop up mainly in production. This is just another consequence of the nature of threading.

Unfortunately, unless you know what you are looking for, it’s nearly impossible to pinpoint this root cause when facing so many random errors and strange behaviours; at least in a reasonable amount of time, while the gates of hell are open in the production environment.

The solution

One solution is to avoid object-level properties altogether and simply stick to local variables; however, it’s inconvenient to keep passing plugin parameter objects around.

Copy to Clipboard

Another solution would be to define a separate class that stores the plugin parameters as properties and refer to them anywhere in the class.

Copy to Clipboard

Swift tumble into insanity

The issue

Sometimes business requirements push us towards hiding internal exceptions and handling them internally, unbeknownst to the user. This is fine anywhere other than plugins.

As mentioned before, due to the ‘transaction’ nature of execution of plugins, a service is linked to a DB transaction. When an exception occurs during the Dynamics service call, the plugin pipeline rolls back the transaction. Any further access to the same transaction causes the highly infamous ‘There is no active transaction. This error is usually caused by custom plug-ins that ignore errors from service calls and continue processing.’

This code snippet shows how swallowing an exception looks like:

Copy to Clipboard

This error is extremely hard to trace because little information is given as to its source. On the bright side, this error is a bit more consistent than the previous issue; so it’s a little easier to trace.

The solution

Completely avoid swallowing exceptions around any IOrganisationService access by using throw in a catch block. As a matter of fact, it’s bad practice in programming, in general, to swallow exceptions unless absolutely necessary.

Copy to Clipboard

Double-edged sword death

The issue

In the modern age of programming, we have been blessed by processors that can handle a ludicrous amount of parallel processing. It is ever so tempting to parallelise whenever possible. I am guilty of such a sinful tendency (sinful because of how fast it could turn on you).

Due to the inherent thread-unsafe and highly managed nature of plugins, it’s prohibited to use threading in plugins. Even simply ‘locking’ is prohibited as well.

I would describe this issue as ‘a death by a thousand cuts.’

The solution

While writing plugins, throw away any ‘threading’ thoughts out the window. If absolutely necessary and beneficial to use threads, move the logic to a web service and call said service from the plugin.

Resources

Guidelines document

Refer to this article for more best practices:  link.

Articles

Develop stateless plugins

Do not use parallel execution

Leave A Comment