Issues with class level declaration in CRM 4 Plugins and Workflows

By | October 22, 2010

It is important that you avoid class level declaration and initialization of variables in Plugins and Workflows.
Let us discuss the issues that one can face with Plugins. Plugins as you know is executed synchronously. But if there happen to be multiple calls made to the same plugin assembly and class, a new object for the Plugin is not always created. Instead the same object is reused. So if you have initialized a class level variable at the start and not in the Execute method, it will be initialized only once at the start and there after for subsequent calls the plugin will reuse the last value stored in the variables much like the static variables.
An example of this would be
public class MyPlugIn : IPlugin
{
ICrmService _service = null;
Bool _myBool = false;
public void Execute(IPluginExecutionContext context)
{
//Check if particular field contains data then set the _myBool as true
{
_myBool = true;
}
//Update record with _myBool Field.
}
}
_mybool is initialized to be false only once at the start. If for some execution the condition succeeds and the value is set to true. The next call to this plugin would use the _mybool value as true by default not initialize it to false.
This behavior can be replicated using the Bulk Edit feature of Dynamics CRM. If you have a plugin registered for the Update method and you use the Bulk Edit feature to update multiple records at one go, the plugin would fire for each of the records modified but the _mybool variable will only be initialized once.
It would be advisable to ensure that class level variables are initialized in the Execute method each time.
Workflow Issues:
If you happen to declare the ICrmService variable at class level in a workflow assembly and make a call to that assembly in step 1 of the workflow and have step 2 that performs another action in the same workflow, the workflow would go into Waiting and not close with a completed or failed status.
The Workflow error message will display the following error for the Workflow job.
Workflow paused due to error: Unhandled Exception: System.Workflow.Runtime.Hosting.PersistenceException: Type ‘Inogic’ in Assembly ‘Inogic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ is not marked as serializable. at System.Workflow.Runtime.WorkflowExecutor.Persist(Activity dynamicActivity, Boolean unlock, Boolean needsCompensation) at System.Workflow.Runtime.WorkflowExecutor.System.Workflow.ComponentModel.IWorkflowCoreRuntime.PersistInstanceState(Activity activity) at System.Workflow.ComponentModel.Activity.MarkClosed() at System.Workflow.ComponentModel.Activity.MarkCompleted() at System.Workflow.ComponentModel.ActivityExecutionContext.CloseActivity() at System.Workflow.ComponentModel.ActivityExecutorOperation.Run(IWorkflowCoreRuntime workflowCoreRuntime) at System.Workflow.Runtime.Scheduler.Run() Inner Exception: System.Runtime.Serialization.SerializationException: Type ‘Inogic’ in Assembly ‘Inogic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ is not marked as serializable. at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo() at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph) at System.Workflow.ComponentModel.Activity.Save(Stream stream, IFormatter formatter) at System.Workflow.ComponentModel.Activity.Save(Stream stream) at System.Workflow.Runtime.Hosting.WorkflowPersistenceService.GetDefaultSerializedForm(Activity activity) at Microsoft.Crm.Workflow.CrmWorkflowPersistenceService.SaveWorkflowInstanceState(Activity rootActivity, Boolean unlock) at System.Workflow.Runtime.WorkflowExecutor.Persist(Activity dynamicActivity, Boolean unlock, Boolean needsCompensation)
This is again because of the declaration of the CRM service object at the class level. The variable is not released after the completion of job if the variable is defined at class level and hence conflicts with the steps that follow in the same workflow job.
[CrmWorkflowActivity(“Customer Search”)] Public partial class CustomerSearch: Activity
{
public static ICrmService crmservice = null; // or make it as function level reference
…… Execute(…….) { }
………..
}
Moving the ICrmService object declaration from Class level to the Execute method ensure that the object is released upon the completion of the job.
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
ICrmService crmservice = null;
Processing…..
}
hopefully this solution will help you to avoid custom WF error. Any feedback on this blog is commendable.