Skip to content

SharePoint Workflow OnTaskChanged Invoked Event Sender Parameter Is Null

Just when you think you’ve seen it all, you run into something that makes you challenge what you think you know. Today’s SharePoint Workflow challenge was a bit of code that was behaving badly — but for reasons that didn’t make any sense. When I dug into it I found that my invoked method for a OnTaskChanged activity was null. In order to understand the importance of this fact I have to do a quick review.

In simple workflows the design time model and the run time model are the same. So you can have an activity named Foo and you can reference it with Foo in your code. That’s easy. The problem is when you introduce a loop, a replicator, or anything else that might cause activities to be replicated — and therefore you runtime model and your design time model no longer match. As soon as this happens you can’t use the activity name. You have to start referencing things based on the sender that your event receives as its first parameter.

I’m working on a parallel approval workflow which uses a custom sequence activity to hold the instance data for each instance of a replicator. To get to my instance information I walk up the tree from my sender to find my specific sequence activity which I know is the parent — and the activity that holds my instance data. This works great right up and until I don’t get my sending activity in the sender parameter. Without it I’m completely lost as to where I am in the workflow.

The solution is to add a code activity after OnTaskChanged and use the ExecuteCode event on the code event. This event will get its sender and can therefore locate itself in the hierarchy.

Replication, Workflow Serialization Problems and DependencyObject

I’ve been working on a workflow that needs to do parallel approval and therefore I have a replicator in the workflow. One of the things that I did was used the pattern of a custom sequence activity inside the replicator to hold the instance data. The custom sequence ends up with a custom property that is set by utilizing the ChildInitialized event of the replicator. You can see Tom Lake’s detailed explanation of this process in the forums: http://social.msdn.microsoft.com/Forums/en-US/windowsworkflowfoundation/thread/ca034011-d2f7-407b-90c5-d0303f753f50

I was using this to manage my set of tasks for the parallel approval. The problem is I was trying to be “nice” and use dependency properties in a class. So my SequenceActivity contained a reference to my class ApprovalTask. The ApprovalTask class contained the properties for the TaskId and TaskProperties (that are needed for managing a task.) To be nice I made these dependency properties which meant that I had to derive ApprovalTask from DependencyObject. To make a long story short, there’s something wrong with deriving from DependencyObject that causes the serialization of the workflow to get screwed up. If you do an OnTaskChanged in your workflow (which makes sense if you want to see if the task was approved or not) you’ll get an error similar to the following in the ULS:

Engine RunWorkflow: System.Workflow.Activities.EventDeliveryFailedException: Event “OnTaskChanged” on interface type “Microsoft.SharePoint.Workflow.ITaskService” for instance id “397f2b91-156c-4de2-b07b-6cae2fefd072” cannot be delivered. —> System.ArgumentNullException: Value cannot be null.     at System.Workflow.ComponentModel.DependencyObject.FixUpMetaProperties(DependencyObject originalObject)     at System.Workflow.ComponentModel.DependencyObject.FixUpMetaProperties(DependencyObject originalObject)     at System.Workflow.ComponentModel.Activity.FixUpMetaProperties(DependencyObject originalObject)     at System.Workflow.ComponentModel.CompositeActivity.FixUpMetaProperties(DependencyObject originalObject)     at System.Workflow.ComponentModel.Activity.Load(Stream stream, Activity outerActivity, IFormatter formatter)     at System.Workflow.ComponentModel.Activity.Load(Stream stream, Activity outerActivity)     at System.Workflow.Runtime.Hosting.WorkflowPersistenceService.RestoreFromDefaultSerializedForm(Byte[] activityBytes, Activity outerActivity)     at Microsoft.SharePoint.Workflow.SPWinOePersistenceService.LoadWorkflowInstanceState(Guid instanceId)     at System.Workflow.Runtime.WorkflowRuntime.InitializeExecutor(Guid instanceId, CreationContext context, WorkflowExecutor executor, WorkflowInstance workflowInstance)     at System.Workflow.Runtime.WorkflowRuntime.Load(Guid key, CreationContext context, WorkflowInstance workflowInstance)     at System.Workflow.Runtime.WorkflowRuntime.GetWorkflow(Guid instanceId)     at System.Workflow.Activities.WorkflowMessageEventHandler.EventHandler(Object sender, ExternalDataEventArgs eventArgs)     — End of inner exception stack trace —     at System.Workflow.Activities.WorkflowMessageEventHandler.EventHandler(Object sender, ExternalDataEventArgs eventArgs)     at Microsoft.SharePoint.Workflow.SPWinOETaskService.RaiseEvent(SPWinOeWorkflow workflow, SPWorkflowEvent workflowEvent, Object workItem, IPendingWork workHandler)     at Microsoft.SharePoint.Workflow.SPWinOeHostServices.Send(SPWinOeWorkflow winoeworkflow, SPWorkflowEvent e)     at Microsoft.SharePoint.Workflow.SPWinOeEngine.RunWorkflow(Guid trackingId, SPWorkflowHostService host, SPWorkflow workflow, Collection`1 events, TimeSpan timeOut)

The reason is because when ApprovalTask came back from serialization it’s SPWorkflowTaskProperties property was blank. The solution? Don’t derive from DependencyObject, mark my ApprovalTask as Serializable, and convert the properties into regular properties. This made the problem go away.