Crm 2011: Custom workflow activity, errors when running in parallel
-
Tuesday, May 15, 2012 6:28 AM
Hy
I have a Custom Workflow, which I can trigger on-demand. This workflows starts a sub-workflow. This sub-workflow starts a custom workflow activity (assembly).
When the Custom On-Demand Workflow is triggered more than once in a short period of time, when another instance is already running, we have several errors:
- InvalidPointer Exceptions
- ValidateOpen - Encountered disposed CrmDbConnection when it should not be disposed
- Unhandled Exception: Microsoft.Xrm.Sdk.InvalidPluginExecutionException: An error occurred in Inactivate.Execute during a service call: ValidateClosed - Db GetCreateConnection() should be closed on End
-...So there seems to be problems when more than one instance is running in parallel. What could cause this problems?
Thanks for your help,
Peter
All Replies
-
Tuesday, May 15, 2012 6:42 AM
Hi Peter,
What are you doing inside the workflow? Do you have any custom workflow activities inside both parent and child workflows?
Does your workflow make use of CrmDbConnection class at all?
Dimaz Pramudya - CRM Developer - CSG (Melbourne) www.xrmbits.com http://twitter.com/xrmbits
-
Tuesday, May 15, 2012 7:01 AM
Hy
I've a On-Demand WF called "Trigger". In this workflow I have two sub-workflows (Child-Workflows)
- Child-Workflow "Child A", inside this workflow a Custom Workflow Activity "Custom Activity A" is called
- Child-Workflow "Child B", inside this workflow a Custom Workflow Activity "Custom Activity B" is called
Inside the Custom Workflow Activities, I do several things with the OrganizationServiceContext, like Queries etc, but I use also for example the IOrganizationService to create Entities. I'm also using early-bound classes, generated with crmsvcutil.
I dont use CrmDBConnection class, I don't even know this class...
Thanks for your help,
Peter
-
Tuesday, May 15, 2012 2:46 PMModerator
Can you post how you get the OrganizationServiceContext and IOrganizationService from your custom activity? It will cause problems if you share those as members of the class instead of regenerating it everytime in the Execute method
Gonzalo | gonzaloruizcrm.blogspot.com
-
Wednesday, May 16, 2012 12:25 PM
Here my Code:
BaseWorkflow-Class
protected ITracingService Tracer
{
get
{
if (tracer == null) throw new InvalidPluginExecutionException("An error occurred in BaseWorkflow: Property 'Tracer' is not initialized.");
return tracer;
}
// set { tracer = value; }
}
protected IWorkflowContext Context
{
get
{
if (context == null) throw new InvalidPluginExecutionException("An error occurred in BaseWorkflow: Property 'Context' is not initialized.");
return context;
}
// set { context = value; }
}
protected IOrganizationService Service
{
get
{
if (service == null) throw new InvalidPluginExecutionException("An error occurred in BaseWorkflow: Property 'Service' is not initialized.");
return service;
}
// set { service = value; }
}
protected OrganizationServiceContext OrgContext
{
get
{
if (orgContext == null) throw new InvalidPluginExecutionException("An error occurred in BaseWorkflow: Property 'OrgContext' is not initialized.");
return orgContext;
}
// set { orgContext = value; }
}
protected CodeActivityContext ActContext
{
get
{
if (actContext == null) throw new InvalidPluginExecutionException("An error occurred in BaseWorkflow: Property 'ActContext' is not initialized.");
return actContext;
}
// set { actContext = value; }
}protected override void Execute(CodeActivityContext executionContext)
{
try
{
//Create Context
context = executionContext.GetExtension<IWorkflowContext>();
//Create tracing service
tracer = executionContext.GetExtension<ITracingService>();
//Create Service and OrgContext
IOrganizationServiceFactory factory = executionContext.GetExtension<IOrganizationServiceFactory>();
var type = Type.GetType("Microsoft.Crm.Workflow.SynchronousRuntime.WorkflowContext, Microsoft.Crm.Workflow, Version=5.0.0.0");
if (type != null)
type.GetProperty("ProxyTypesAssembly").SetValue(factory, typeof(Admission).Assembly, null);
service = factory.CreateOrganizationService(context.UserId);
orgContext = new OrganizationServiceContext(service);
actContext = executionContext;
RunWorkflow();
}
catch()
{}
}
ActivityX-Class
public class ActivityX : BaseWorkflow
{
protected override void RunWorkflow()
{
xxx= (from c in OrgContext.CreateQuery<xxx>()
where c.Id == xxxId
select c).SingleOrDefault();
}
}...how can I solve this problem?
Kind regards,
Peter -
Wednesday, May 16, 2012 12:33 PM
Hi Peter,
Generally speaking, both plugins and workflows in CRM really doesn't behave well when you have IOrganizationService, ITracingService and the Execution context defined as instance variables.
These will need to be defined as a local variable inside the Execute method and get passed around as needed. So your Execute method would look something like this. And make sure to get rid of your instance variables.
protected override void Execute(CodeActivityContext executionContext) { //Create Context var context = executionContext.GetExtension<IWorkflowContext>(); //Create tracing service var tracer = executionContext.GetExtension<ITracingService>(); //Create Service and OrgContext IOrganizationServiceFactory factory = executionContext.GetExtension<IOrganizationServiceFactory>(); var type = Type.GetType("Microsoft.Crm.Workflow.SynchronousRuntime.WorkflowContext, Microsoft.Crm.Workflow, Version=5.0.0.0"); if (type != null) type.GetProperty("ProxyTypesAssembly").SetValue(factory, typeof(Admission).Assembly, null); var service = factory.CreateOrganizationService(context.UserId); var orgContext = new OrganizationServiceContext(service); RunWorkflow(context, tracer, service, orgContext); }This is due to the way CRM instantiate the plugin and custom workflow activities. When plugin / custom workflow activity runs, CRM might reuse existing instance instead of creating new ones. When this happen, things goes bizarre because the same instance of IOrganizationService & ITracingService are not meant to be shared across different pipelines.
I hope this helps. If my response answered your question, please mark the response as an answer and also vote as helpful.
Dimaz Pramudya - CRM Developer - CSG (Melbourne) www.xrmbits.com http://twitter.com/xrmbits
- Proposed As Answer by Dimaz Pramudya (www.xrmbits.com) Wednesday, May 16, 2012 12:33 PM
- Edited by Dimaz Pramudya (www.xrmbits.com) Wednesday, May 16, 2012 12:33 PM
-
Wednesday, May 16, 2012 12:49 PMModerator
As Dimaz points out, you are getting into SQL errors because you are sharing the context among different instances of your custom workflow activity (from different workflows). Each context has its own SQL transaction context, you can really not mix them up into a base class or class properties, they need to be retrieved each time your Execute method is called.
This is a really common mistake in plugins / workflow activities ;)
Gonzalo | gonzaloruizcrm.blogspot.com
- Proposed As Answer by Gonzalo Ruiz RMVP, Moderator Wednesday, May 16, 2012 12:49 PM
-
Wednesday, May 16, 2012 12:56 PM
Gonzalo,
Yep. I've seen way to many code where the developers are putting things as instance variables.
Maybe a good topic for a blog post, eh? :)
Dimaz Pramudya - CRM Developer - CSG (Melbourne) www.xrmbits.com http://twitter.com/xrmbits
-
Thursday, May 17, 2012 11:54 AM
Hi Peter,
Has the issue been resolved?
If my response answered your question, please mark the response as an answer and also vote as helpful so that it helps others finding the answer easily.
Dimaz Pramudya - CRM Developer - CSG (Melbourne) www.xrmbits.com http://twitter.com/xrmbits
- Proposed As Answer by Dimaz Pramudya (www.xrmbits.com) Thursday, May 17, 2012 11:54 AM
-
Thursday, March 14, 2013 4:43 PM
Gonzalo, you stated this is a common mistake, but it is the way Microsoft implemented the ActivityBase in the Workflow template from the CRM 2011 SDK Developer Toolkit...are you saying this is flawed, and how would you fix it? Would you remove the public property and make it local to the Execute method? I'm having the same problem as Peter and would appreciate your input.
public abstract class ActivityBase : CodeActivity
{
public XrmServiceContext ServiceContext { set; get; }
protected abstract string ActivityName { get; }
protected abstract void WorkflowSpecificStep(CodeActivityContext executionContext, ITracingService tracingService, IWorkflowContext context, IOrganizationService service);
protected override void Execute(CodeActivityContextexecutionContext)
{
ITracingService tracingService = null;
try
{
// Create the tracing service
tracingService = executionContext.GetExtension<ITracingService>();
if (tracingService == null) throw new InvalidPluginExecutionException(string.Format("Failed to retrieve tracing service in {0}.", ActivityName));
tracingService.Trace("Entered {0}.Execute(), Activity Instance Id: {1}, Workflow Instance Id: {2}",ActivityName, executionContext.ActivityInstanceId, executionContext.WorkflowInstanceId);
// Create the context
IWorkflowContext workflowContext = executionContext.GetExtension<IWorkflowContext>();
if (workflowContext == null) throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
tracingService.Trace("{0}.Execute(), Correlation Id: {1}, Initiating User: {2}", ActivityName, workflowContext.CorrelationId, workflowContext.InitiatingUserId);
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
if (serviceFactory == null) throw new InvalidPluginExecutionException("Failed to retrieve organization service factory.");
IOrganizationService service = serviceFactory.CreateOrganizationService(workflowContext.UserId);
if (service == null) throw new InvalidPluginExecutionException("Failed to create organization service.");
if (ServiceContext == null) ServiceContext = new XrmServiceContext(service); // get the rich service context
WorkflowSpecificStep(executionContext, tracingService, workflowContext, service); // calling the specific step in the derived class
tracingService.Trace("Exiting {0}.Execute(), Correlation Id: {1}", ActivityName, workflowContext.CorrelationId);
}
catch (FaultException<OrganizationServiceFault> e)
{
tracingService.Trace("Exception: {0} happened in {1}", e.ToString(), ActivityName);
// Handle the exception.
throw;
}
}
}