Member, Static variable and Thread safety in Plug-in for CRM 4.0

Member, Static variable and Thread safety in Plug-in for CRM 4.0

Are you looking for best practices in plug-in development? Are you seeing data inconsistency and slow performance issues in the plug-in intermittently? Are you having problems with CRM 3.0 plug-in showing inconsistent/unknown Data after upgrade? If the answer is YES, please continue reading this article.

A CRM 4 Plug-in is an implementation of the IPlugin interface, which means you need to implement the Execute method, which will be called by CRM when the action gets triggered. Although this may appear straight forward, plug-in performance and thread safety plays a vital role based on our implementation of the plugin infrastructure.

How CRM instantiates the plug-in?

CRM instantiates the Plug-in class on the first activation of the plug-in. CRM will call plug-in's constructor. Once the object is instantiated, CRM calls the Execute method on the object. For performance reasons, CRM DOES NOT dispose of the object after it completes execution. So CRM caches the object and calls Execute on the same object instance on the next activation of the plug-in. CRM will flush its cache when any properties are changed on the pluginassembly, plugintype, sdkmessageprocessingstep, sdkmessageprocessingstepimage entities for registration. This means that you need to be very careful when defining class-level variables in the plug-in class as multiple threads can execute the code at one time.

Let me explain this with a MyCounter plugin example and show the State-full behavior of the plug-in. Register the following plugin on Account update so that you get a task created on every update of account.

1:publicclass MyCounter : IPlugin

2: {

3:string Config;

4:string SecureConfig;

5:int Counter;

6: PropertyBag InputParams_Data= null;

7:string NonCorruptValue;

8:

9:public MyCounter(string config, string secureConfig)

10: {

11: Config = config;

12: SecureConfig = secureConfig;

13: Counter= 0;

14:// Assigned in Constructor and not changed later

15: NonCorruptValue = "This value is not corrupted";

16: }

17:publicvoid Execute(IPluginExecutionContext context)

18: {

19:// Increments the Counter

20:// This is NOT Thread safe as 2 threads can increment the Counter variable (stored once in memory) at the same time

21: Counter++;

22:// DON'T do this

23: InputParams_Data= context.InputParameters;

24:string inputSerialized_Unknown = getSerializedInputParams();

25:

26: task t = new task();

27: t.subject = "Counter " + Counter.ToString();

28:// NonCorruptValue is thread safe as you just read but never assign except the constructor

In CRM 3.0, callouts were instantiated every time for every activation, which means that class-level variables defined in the callout would not be shared across multiple instances of the callout class, since CRM would allocate memory for each instance of the callout and dispose it after execution completed.

For example, the above callout will NOT see data corruption on the EntityXml variable in CRM 3.0. As soon as the callout is upgraded to CRM 4.0, the variable data will be unpredictable. There are a couple of ways to work around this issue.

2. If it is harder to make these code changes, you can implement a Proxy class that will make the callout stateless. Once you have created the Proxy class, remember to change the callout.config.xml to point to the new Proxy class.

Under the topic “Writing a Plug-in”, the CRM SDK documentation states:

"Note: For improved performance, Microsoft Dynamics CRM caches plug-in instances. The plug-in's Execute method should be written to be stateless as the constructor is not called for every invocation of the plug-in. All per invocation state information is stored in the context. This means that you cannot use global variables in plug-ins."

As far as upgrading callouts from CRM 3.0 to 4.0, I have filed a documentation bug to include the callout upgrade information from the blog in a future release of the SDK.