Unknown's avatar

Posts by Simon Doy

I am an avid SharePoint enthusiast who works as an Independent SharePoint Consultant based in Leeds, United Kingdom. I am one of the organisers of the Yorkshire SharePoint User Group in the United Kingdom. I have been designing and building SharePoint solutions since 2006.

>Custom Exception Objects in Workflows


>The more you use Windows Workflow Foundation, the more you get to understand it. In the past, I have been a bit lazy with exception handling and just used out of the box .NET exception types.

Anyway to improve the resilency of a workflow I decided to implment a custom exception. Pretty straight forward I thought.

However when I got to test it the exception was raised correctly and all looked well an error appeared in the workflow history. However when the workflow went to rehydrate it errored out with the following error:-

System.Workflow.Activities.EventDeliveryFailedException: Event “OnTaskChanged”
on interface type “Microsoft.SharePoint.Workflow.ITaskService” for instance id
“80742821-239e-4740-b19b-7ad2885ff6ba” cannot be delivered. —>
System.Runtime.Serialization.SerializationException: The constructor to
deserialize an object of type
‘ITSP.CC.SimpleApprovalWorkflow.TaskNotUpdatedByAssignedUser’ was not found.
—> System.Runtime.Serialization.SerializationException: The constructor to
deserialize an object of type
‘ITSP.CC.SimpleApprovalWorkflow.TaskNotUpdatedByAssignedUser’ was not
found. at
System.Runtime.Serialization.ObjectManager.GetConstructor(Type t, Type[]
ctorParams) at
System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object
obj, Serializatio…

Anyway I wrote a document which has been used as the base of this post so here it is.

What do I do now?
This article will describe a couple of issues that were discovered when implementing custom exceptions within a SharePoint / Windows Workflow Foundation workflow.
The custom exception was used in a fault handling activity and assigned as one of the types of exceptions that could be handled by a fault handler.
When selecting the exception you choose the type of exception and also bind a property to the fault property for that exception handler.
This is fine, though workflows generally do not start and finish in one go, they normally have to pause and wait for some user interaction or system interaction. In order to not waste system resources the workflows hydrate (go to sleep) and dehydrate (wake up) when the workflow has done all the work that it needs to do.
When the workflow hydrates and dehydrates it calls each of the objects serialization routines. This is normally fine as in order to make an object serializable we add the [Serializable] attribute above the class definition.
Now the workflow is able to serialize the class … great.

Great?
Well not quite this works brilliantly when all the properties (member variables) are public as the serialization engine just serializes them, though for member variables that are protected or private the engine needs some assistance in case it records some data, making it public when its really sensitive.
So lets not do anything and see what happens… well when the workflow wakes up to process an event an error occurs:-

System.Workflow.Activities.EventDeliveryFailedException: Event “OnTaskChanged”
on interface type “Microsoft.SharePoint.Workflow.ITaskService” for instance id
“80742821-239e-4740-b19b-7ad2885ff6ba” cannot be delivered. —>
System.Runtime.Serialization.SerializationException: The constructor to
deserialize an object of type
‘ITSP.CC.SimpleApprovalWorkflow.TaskNotUpdatedByAssignedUser’ was not found.
—> System.Runtime.Serialization.SerializationException: The constructor to
deserialize an object of type
‘ITSP.CC.SimpleApprovalWorkflow.TaskNotUpdatedByAssignedUser’ was not
found. at
System.Runtime.Serialization.ObjectManager.GetConstructor(Type t, Type[]
ctorParams) at
System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object
obj, Serializatio…

To fix this we need to provide a constructor to deserialize the object and also a method so that the runtime and properly serialize the object.
Serialization support is provided by implementing the ISerializable interface and this interface requires to functions.
These are handled by the following functions.

public virtual void GetObjectData(SerializationInfo info, StreamingContext
context)·

protected MyObject(SerializationInfo info,
StreamingContext context)

GetObjectData is called to get the serialization data and this is where we tell the object to also put the private / protected member variable definitions.
The protected constructor provides a way to get the serialized information in SerializationInfo object back into the private/protected member variables.

Now with the Custom exceptions we have added complexity in that the System.Exception object already implements the ISerialize interface so we need to override these functions and also call the base functions whilst handing our custom exception objects data.
The example below describes this:-

The deserization constructor is as follows:

protected TaskNotUpdatedByAssignedUser(SerializationInfo info, StreamingContext
context)

:
base
(info,context)

{

m_sUser
=
info.GetString(“m_sUser”);

m_sAssignedUser =
info.GetString(“m_sAssignedUser”);

m_sErrorMessage =
info.GetString(“m_sErrorMessage”);

}

The GetObjectData function is as follows:-

[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue(“m_sUser”, m_sUser);
info.AddValue(“m_sAssignedUser”, m_sAssignedUser);
info.AddValue(“m_sErrorMessage”, m_sErrorMessage);
}

Once these functions have been implemented and the new code loaded then the workflow rehydrates as expected.

One tip:

remember to recycle the timer service as well as the app pool.

Use:

net stop sptimerv3 and net start sptimerv3.

Hope someone finds this useful!

>Using Contact Selector in a Modification InfoPath Form


>

As always things are never as simple as you thought they would be. This post is a case in point, so I have been building a workflow which allows a user to approve a list item. This workflow is built with VS2005 and with previous workflows, the modification forms have worked beautifully.

Well that is until I wanted to include the contact selector inside the modification form. As I mentioned the workflow allows a user to approve the list item, however what if the user is away or want someone else to approve it. Well then a simple workflow modification should sort that, shouldn’t it?!

In order to create a workflow modification you need to do the following:-

  1. Create a form to use for your modification
  2. Update the workflow xml with a modification tag which has a unique GUID and contains the URN of the form that the workflow is going to use.
  3. Implement a EnableWorkflowModification which references the GUID that it is modifying, this is the part where the problem existed (more on that later).
  4. Implement a Event Listener activity so that when the modification occurs there is something listening for it!
  5. Finally implement the handler which updates the workflow and related objects so that the modifications are made.
We will not cover points 1 and 2 as they are discussed in this great post ([insert url]).

So starting with point 3 firstly, so what does an EnableWorkflowModification activity do?

Well it should log that the modification has been activated, it also then assigns the default data that should past into the modification form. Now this is the interesting part, how do you make the xml form data that the InfoPath form requires?

Using a tool called xsd which is part of the VS2005 toolkit. Basically with every InfoPath form there is a XML schema created (.xsd file) if we call xsd /c with the path of the XSD then a code class will be created which is used for serialization/deserialization.

This is used within the EnableWorkflowModification activity to generate the xml ContextData which is then loaded into the Modification form.

In our example we use the serialization class to create a class of the form and then assign values to the various properties of the class. We can then serialize this class to make the xml.

This would be achieved like so:-

ReassingForm reassignForm=new ReassingForm();

reassignForm.ReassingComments = "The workflow is being reassinged from " + m_sApproverLogin;
reassingForm.Approver = ApproverArray;

Here we are assigning the current approver and also uploading some comments to the form, which the user can then update as required.

Finally the class needs to be serialized :-

Now when the workflow modification kicks in then a user gets a link in the workflow status page allowing them to perform the modification. When this is clicked then the Context Data is loaded into the InfoPath form and the InfoPath form is displayed.

When the contact selector is used an error will occur complaining about the fact that the my namespace prefix is not found. So how do we get around this?

We need to tell the serialization method that it should add my prefixes so the xml instead of being like this :-

is like this:-
To do this we need to use the XmlSerializerNamespaces class.
This would look like:-

XmlSerializerNamespaces nsReassignForm = new
XmlSerializerNamespaces();
nsReassignForm.Add(“my”, “http://schemas.microsoft.com/office/infopath/2003/myXSD/2008-05-29T12:53:57“);

and can be used when calling XmlSerializer.Serialize as additional parameter.
This will then fix the error on form and allow the contact selector to be able to process and store the users that you have provided.

Let me know how you get on.