Category Archives: Workflow

Nasty SharePoint Workflow Bug

First off, thanks to my friend Kirk Allen Evans for his help with this problem on a Saturday! Microsoft is lucky to have him. In the process he created a nice screen cast that you can watch to set the background. He doesn’t encounter the bug, but as it is a threading issue he might have simply been lucky (or me unlucky threading bugs are like that), or (I’m hoping) he has a hotfix that I lack.

In some situations, a workflow will enter into a non-error, but unrecoverable state that looks as if an OnTaskCreated workflow activity did not fire even though the task exists. Sample code that reproduces the problem in my environment on both MOSS SP1 And MOSS SP1 + Infrastructure update is located here. If the problem occurs, the workflow will not continue processing beyond the OnTaskCreated activity. You can see reports of this issue as experienced by others here. Scroll down to the bottom of the post for a workaround.

I have a moderately complex workflow than starts three different tasks. A simplified version is shown below.

Intermittently, the workflow will stop working with history that makes it look like the workflow failed to handle one or more OnTaskCreated activities as shown below.

Notice that there is no entry for Task 1 Created (nor will there ever be). This workflow is toast and will not complete.

It turns out that the problem is actually in the workflow manager. If you step through the workflow, you will see every onTaskCreated activity light up in the designer, but the runtime will not invoke the method. If you look at the error log, you will see two exceptions:

RunWorkflow: System.ArgumentException: Item has already been added. Key in dictionary: ‘c6923528-0908-4d75-86ed-f58342ea507e’ Key being added: ‘c6923528-0908-4d75-86ed-f58342ea507e’ at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add) at System.Collections.Hashtable.Add(Object key, Object value) at System.Collections.Hashtable.SyncHashtable.Add(Object key, Object value) at Microsoft.SharePoint.Workflow.SPWorkflowManager.TrackCreatedInstance(Guid trackingId, SPWorkflow workflow) at Microsoft.SharePoint.Workflow.SPWorkflowManager.RunWorkflowElev(SPWorkflow originalWorkflow, SPWorkflow workflow, Collection`1 events, SPRunWorkflowOptions runOptions)   

RunWorkflow: System.Workflow.Activities.EventDeliveryFailedException: Event "OnTaskCreated" on interface type "Microsoft.SharePoint.Workflow.ITaskService" for instance id "c6923528-0908-4d75-86ed-f58342ea507e" cannot be delivered. —> System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.SharePoint.Workflow.SPWorkflowHostServiceBase.LoadInstanceData(Guid instanceId, Boolean& compressedData) 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, CreationCont…   

Apparently, there is a threading issue with the Workflow manager and when it happens, the workflow receives notice that the event is firing, but it can’t load the instance data.

I’m hoping that this is a known issue and that there is a hotfix available. I’ll follow up as I learn more.

In the meantime, you can work around this problem by simply avoiding OnTaskCreated. The requirement I have that led me down this path is to set up permissions on the tasks beyond what the SpecialPermissions property allows (more on that some other day). I will meet that requirement by using a list event handler and apply the permissions outside the workflow via the handler.

Good luck!

–Doug Ware

Code to Associate an Approval Workflow and Make it the Default for Content Approval

Kind of random, but I had this laying around and I figure it might be useful to someone. I got the XML by configuring an Approval workflow and using SharePoint Manager to copy the property value. Note: this assumes you are running MOSS and already have a Tasks list and Workflow History list.

static
string _associationXml = @"

<my:myFields xml:lang=’en-us’ xmlns:xsi=’http://www.w3.org/2001/XMLSchema-instance’ xmlns:my=’http://schemas.microsoft.com/office/infopath/2003/myXSD’>

<my:Reviewers>

<my:Person>

<my:DisplayName>A Site Members</my:DisplayName>

<my:AccountId>A Site Members</my:AccountId>

<my:AccountType>SharePointGroup</my:AccountType>

</my:Person>

</my:Reviewers>

<my:CC></my:CC>

<my:DueDate xsi:nil=’true’></my:DueDate>

<my:Description>Please review this document.</my:Description>

<my:Title></my:Title>

<my:DefaultTaskType>1</my:DefaultTaskType>

<my:CreateTasksInSerial>true</my:CreateTasksInSerial>

<my:AllowDelegation>true</my:AllowDelegation>

<my:AllowChangeRequests>true</my:AllowChangeRequests>

<my:StopOnAnyReject xsi:nil=’true’></my:StopOnAnyReject>

<my:WantedTasks xsi:nil=’true’></my:WantedTasks>

<my:SetMetadataOnSuccess>false</my:SetMetadataOnSuccess>

<my:MetadataSuccessField></my:MetadataSuccessField>

<my:MetadataSuccessValue></my:MetadataSuccessValue>

<my:ApproveWhenComplete>false</my:ApproveWhenComplete>

<my:TimePerTaskVal xsi:nil=’true’></my:TimePerTaskVal>

<my:TimePerTaskType xsi:nil=’true’></my:TimePerTaskType>

<my:Voting>false</my:Voting>

<my:MetadataTriggerField></my:MetadataTriggerField>

<my:MetadataTriggerValue></my:MetadataTriggerValue>

<my:InitLock>false</my:InitLock>

<my:MetadataStop>false</my:MetadataStop>

<my:ItemChangeStop>false</my:ItemChangeStop>

<my:GroupTasks>false</my:GroupTasks>

</my:myFields>";

 

static
void Main(string[] args)

{


//Get the site, web, and list


SPSite site = new
SPSite(@"http://YourUrlHere");


SPWeb web = site.RootWeb;


SPList docs = web.Lists["Your List Here"];

 


//Get the workflow template


SPWorkflowTemplate approvalTemplate =

web.WorkflowTemplates.GetTemplateByName("Approval",

System.Globalization.CultureInfo.CurrentCulture);

 


//Create the association


SPWorkflowAssociation assoc =


SPWorkflowAssociation.CreateListAssociation(

approvalTemplate,


"Approval Workflow",

web.Lists["Tasks"],

web.Lists["Workflow History"]);

 


//Set the startup options

assoc.AllowManual = true;

assoc.AutoStartCreate = true;

 


//Provide the association data

assoc.AssociationData = _associationXml;

 


//Apply the association to the document library

docs.AddWorkflowAssociation(assoc);

 


//Enable moderation and make the workflow the


//default approval workflow

docs.EnableModeration = true;

docs.EnableVersioning = true;

docs.EnableMinorVersions = true;

docs.DefaultContentApprovalWorkflowId = assoc.Id;

docs.Update();

 


Console.WriteLine("Done!");


Console.ReadLine();

}

Author: Doug Ware

Most Excellent ASP.Net Workflow Forms Tutorials

Last week I was searching in vain for an updated version of an old sample I had back in the B2TR days when I stumbled across a comment in the ECM team blog from Robert Shelton. He has written what I believe is the best series of articles on developing workflows with ASP.Net forms (as opposed to InfoPath) that I have seen anywhere.

You can find them here: http://www.sheltonblog.com/archive/2007/10/05/sharepoint-document-workflow-with-visual-studio-workshop-documents-download.aspx

I normally don’t post links, but a search on "ASP.Net workflow forms tutorial" has this site many pages back and these tutorials should be the first link on page 1.

Update: This is why you shouldn’t blog after working 16 hours. Simply adding ‘WSS’ to the search phrase shows he is number 1 on page 1. J Which is good because he deserves it!

Easy WSS Forms with Workflow and Anonymous Access

I am just having loads of fun with the anonymous membership provider. I mentioned in a previous post that the provider approach solves a wider range of issues than merely allowing anonymous types to upload files to a document library.

For example, you can’t create a workflow that is invoked by an anonymous user in WSS. Here is a thread on the MSDN forums about the issue. The solution the developer came up with was to muck about with the list forms on the list in question, just as I was contemplating before Søren Nielsen bought me a ticket on the clue train with his suggestion of a custom provider.

As I announced a few days ago, we are now signing up folks for the SharePoint developer core skills classes in November and December. I thought it would be nice to create a form to allow folks to request more information. Now that I have the ability to do work flows without authentication, I thought it would be fun to create the form using plain old WSS workflow via SharePoint Designer instead of creating a web part or user control. As it turns out, this is a lot easier than creating, deploying, and building a web part page using a control.

You can see the result here. If you fill out the form and hit ‘Submit Request’ I will get an email.

It’s worth noting that this technique works well for creating a form quickly even when the anonymous scenario does not exist. If you need a quick and easy data entry form for WSS, follow the steps below and ignore the bits about Anon. Just make sure your users have permissions to contribute to the list that starts the work flow and if the work flow adds items to any other lists then those as well!!!

How it’s done

The first thing you need to do is add Anon to the site’s Visitors group. WSS workflow is, to put it charitably, picky about permissions. Even though I had given Anon specific access to the list where the workflow was attached, it didn’t work until I added Anon to the site. It occurs to me I probably should have done that anyway. And so, even though, it was one of the last things I did, just save yourself some trouble if you set the provider up previously and do that now if you haven’t already.

Anyway…

Workflow in WSS is designed to work against list items. A workflow can be started manually, when an item is created, or when an item changes. Whichever way, if you want a workflow you need a list item and to have a list item, of course, you need a list. Therefore step one in this exercise is to create a custom list and add an item.

  • Create a new custom list

  • Add an item to the list

  • Give Anon ‘Contribute’ permissions to the list


  • Open the site in SharePoint Designer
  • Form the File menu, choose New | Workflow
  • The Workflow Designer will open. Fill the form out as follows:
  • Click the Initiation button. This is where we will define the fields for our form.
  • Add the following fields:

Your Name

Single line of text

 

Company

Single line of text

 

eMail

Single line of text

 

Phone

Single line of text

 

Contact Preference

Choice (menu to choose from)

Class

Choice (menu to choose from)

The parameters form should now look like this:

Click OK

  • Click Next on the Workflow Designer and then click Actions and choose "Send an email" to add an action. Because we want this workflow to happen every time we will not create any conditions.
  • Click the this message link and fill out the email form as follows except using your own email address!:
  • Add fields from the form the user filled out by clicking Add Lookup to Body. Choose ‘Workflow Data’ from the Source dropdown to populate the Field with the fields from step 8.

    Create an email body that has all of the information and wind up with something like this:

    and click OK.

  • Finally click Finish in the Workflow Designer to deploy the new workflow.
  • At this point you may want to tidy up the workflow form a bit. In my case I wanted to change the text on the buttons and do some styling. You can find it in the folder list in SharePoint Designer and edit it as you would any form. However, you will want to back up any edits you make because if you edit the workflow SPD will regenerate the form and stomp your changes!
  • Great! So now we have all the pieces we need. To finish it up we need to create a link to our new form and put it somewhere. We can’t simply link to the form itself however because the workflow requires a list item to attach itself to. To get the link, navigate to the list created in step 1 and choose Workflows from the item dropdown.

  • Right click the Request Workflow link and choose Properties
  • Copy the address, in this case, /Workflows/Request%20Workflow/Request%20Workflow.aspx?List=ea10f5a1-5401-4c96-bcbf-d91db261dd76&ID=1&TemplateID={d4d1f324-bfcf-44f6-8f10-efdd31400650}&Source=http%3A%2F%2Finternal%2Eskinner%2Edemo%2FLists%2FWorkflow%2520List%2FAllItems%2Easpx. That is the url you will use to access the form and it will always work unless you delete the item! I recommend hiding the list to keep anyone from doing it accidentally. If someone is savvy enough and wants to be a jerk they still can get to the item by parsing out the link. So, this does have the disadvantage of not being as robust as a handmade control, but it is way easier. This is one case where SharePoints seemingly arbitrary need to force certain levels of permissions actually make it less secure in my opinion.
  • Test your form by clicking the link and filling it out. If you did everything right you will get an email with all the nice email with all of the information, but you also wound up redirected to that list and that is no good at all! So there is one last thing we need to do to make this solution complete!
  • We can’t fix the redirect on the workflow form because it is actually taking place on the server and we have no way to change it. So instead we have to use some simple java script to do the redirect on the client side.
    Return to SharePoint Designer and edit the AllItems.aspx form for the list.
  • Find the PlaceHolderAdditionalPageHead place holder and change it to include the script.
    <asp:content contentplaceholderid="PlaceHolderAdditionalPageHead" runat="server">

        <SharePoint:RssLink runat="server"/>

        <script>

        alert(‘Thank you for your interest. We will contact you shortly.’);

        window.location = ‘http://www.someotherUrl/’;

        </script>

    </asp:content>

  • Save your work!
  • Finally, (shew!) hide the list from the browser and from the all items list by right clicking the list and choosing properties.
  • Check Hide from Browsers and hit OK

That’s it! Enjoy!!!

Now get out there and build some forms.

Author: Doug Ware