19.11.2008, 00:05 | #1 |
Участник
|
Microsoft Dynamics CRM Team Blog: Member, Static variable and Thread safety in Plug-in for CRM 4.0
Источник: http://blogs.msdn.com/crm/archive/20...r-crm-4-0.aspx
============== 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: public class 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: public void 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 29: t.description = NonCorruptValue + inputSerialized_Unknown; 30: //Creates task with Counter information 31: context.CreateCrmService(true).Create(t); 32: } 33: string getSerializedInputParams() 34: { 35: XmlSerializer serializer = new XmlSerializer(typeof(PropertyBag)); 36: StringWriter writer = new StringWriter(CultureInfo.InvariantCulture); 37: // InputParams data is corrupted if 2 threads call the same IPlugin at same time 38: serializer.Serialize(writer, InputParams_Data); 39: StringBuilder sb = writer.GetStringBuilder(); 40: return sb.ToString(); 41: } 42: } 1. Register the Plug-in to trigger on Update of the Account entityThis proves that CRM only maintains one object in memory and re-uses it every time CRM calls Execute method instead of creating a new instance of the object each time. CRM 3.0 Callouts: Upgrade issue and data inconsistency (Thread safety) 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. 1: public class MyCallout : CrmCalloutBase 2: { 3: string EntityXml = null; 4: public override PreCalloutReturnValue PreUpdate(CalloutUserContext userContext, CalloutEntityContext entityContext, 5: ref string entityXml, ref string errorMessage) 6: { 7: EntityXml = entityXml; 8: //Do some suff 9: entityXml = GetUpdatedStuff(); 10: return PreCalloutReturnValue.Continue; 11: } 12: string GetUpdatedStuff() 13: { 14: //Do Some Stuff; 15: //Update EntityXml; 16: return EntityXml; 17: } 18: } 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. Workarounds 1. Change the CRM 3.0 callout code NOT to use class-level variables, instead pass the information via method parameters. 1: public class MyCallout : CrmCalloutBase 2: { 3: //string EntityXml = null; //REMOVE THIS 4: public override PreCalloutReturnValue PreUpdate(CalloutUserContext userContext, CalloutEntityContext entityContext, 5: ref string entityXml, ref string errorMessage) 6: { 7: entityXml = GetUpdatedStuff(entityXml); //PASS as Parameter 8: return PreCalloutReturnValue.Continue; 9: } 10: string GetUpdatedStuff(string updateXml) //Accept Parameters 11: { 12: //Do Some Stuff; 13: //Update EntityXml; 14: return EntityXml; 15: } 16: } 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. 1: public class MyCalloutProxy : CrmCalloutBase 2: { 3: public override PreCalloutReturnValue PreUpdate(CalloutUserContext userContext, 4: CalloutEntityContext entityContext, ref string entityXml, ref string errorMessage) 5: { 6: MyCallout currentOne = new MyCallout(); 7: return currentOne.PreUpdate(userContext, entityContext, ref entityXml, ref errorMessage); 8: } 9: } The same Proxy class workaround can be used for the CRM 4.0 Plugins if you really want to use the class level variables to store instance data. Summary 1. Do NOT use Member variables to store instance data.Please add your comments on your experience changing the code and suggestions for improving this in CRM 5. Thanks, Ajith Gande Источник: http://blogs.msdn.com/crm/archive/20...r-crm-4-0.aspx
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору. |
|