Showing posts with label workflow. Show all posts
Showing posts with label workflow. Show all posts

18 May 2011

Starting a workflow for the current InfoPath form item in code behind

If you have the requirement to be able to programmatically start a workflow from the code behind an InfoPath form template, then the following code might do the trick (which can be called say from a button click event).

You'll need to have a promoted field in your form template which stores a unique ID for the current form item. You can use the core ID field value provided by SharePoint or perhaps you generate your own unique reference number for the form, whichever you use it needs to uniquely identify the form in the library so we can get the current form item from code using SPQeury and can then spark off the workflow.




private bool StartWorkflow(SPContext ctx)
{
    bool restarted = false;
    string output = "";
    try
    {
        SPList myForms = ctx.Web.Lists["My Forms Library"];
        if (myForms != null)
        {

            ctx.Web.AllowUnsafeUpdates = true;
            SPListItem thisForm = GetCurrentListItem(myForms);
            if (thisForm != null)
            {
                // Get the workflow association template using the workflow name as displayed on the workflow setting page
                SPWorkflowAssociation associationTemplate = prForms.WorkflowAssociations.GetAssociationByName("My Workflow", System.Globalization.CultureInfo.CurrentCulture);
                if (associationTemplate != null)
                {
                    // Now try and start the workflow
                    SPWorkflow newWorkflow = ctx.Site.WorkflowManager.StartWorkflow(thisForm, associationTemplate, associationTemplate.AssociationData);
                    if (newWorkflow != null)
                    {
                        // Success
                        restarted = true;
                    }
                }
            }
        }
    }
    catch (Exception) { }

    return restarted;
}

private SPListItem GetCurrentListItem(SPList myForms)
{
    SPListItem currentItem = null;
    // Get unique ID of current form from a field in form template
    // This ID field must be promoted to the SharePoint list
    string itemId = GetFormFieldValue("/my:myFields/my:MyFormID");
    if (itemId != null && !itemId.Equals(""))
    {
        if (myForms != null)
        {
            SPQuery oQuery = new SPQuery();
            // Query the forms library to get the item where the unique form ID field equals itemId
            // Use the internal field name in the where clause!
            oQuery.Query = string.Format(
                    "{0}"
                    , itemId);
            oQuery.ViewFields = "";
            SPListItemCollection collListItems = prForms.GetItems(oQuery);
            if (collListItems != null && collListItems.Count > 0)
            {
                foreach (SPListItem oListItem in collListItems)
                {
                    // Should only be one result
                    currentItem = oListItem;
                    break;
                }
            }
        }
    }

    return currentItem;
}

private string GetFormFieldValue(string xPath)
{
    XPathNavigator xnDoc = this.MainDataSource.CreateNavigator();
    XPathNavigator xnMyField = xnDoc.SelectSingleNode(xPath, this.NamespaceManager);
    if (xnMyField != null)
    {
        return xnMyField.Value;
    }
    else
    {
        return string.Empty;
    }
}
   

19 November 2010

Assembly versioning for SharePoint Visual Studio Workflow solutions

Problem

You have a Visual Studio SharePoint sequential or state machine workflow solution deployed to your farm and have a number of active workflows. You make some changes to your workflow in development which involves some "interface" changes (e.g. you've added or removed workflow properties, states, state transitions, etc) then build, package and deploy your new workflow version to your farm. You then find that the existing active workflow instances fail (say when you try to complete a workflow task that was pending). The ULS logs show an error along the lines of:

Engine RunWorkflow: System.Workflow.Activities.EventDeliveryFailedException: Event "OnWorkflowItemChanged" on interface type "Microsoft.SharePoint.Workflow.ISharePointService" for instance id "e7903ad3-e939-4211-bf04-5efcd6bd243c" cannot be delivered. ---> System.Runtime.Serialization.SerializationException: The object with ID 250 implements the IObjectReference interface for which all dependencies cannot be resolved. The likely cause is two instances of IObjectReference that have a mutual dependency on each other

This basically implies that the persisted data for that workflow instance cannot be deserialised as it refers to the old version of the assembly interface. Put another way, existing persisted workflow data has a dependency on the existing version of your assembly DLL.

Investigation

Found lots of half answers before stumbling across this MSDN blog article by Manpreet Alag: http://blogs.msdn.com/b/malag/archive/2008/07/16/how-to-upgrade-workflow-assembly-in-moss-2007.aspx. Based on his advice I came up with the following steps to solve this problem.

Solution

The solution to this problem is two fold, so if your new workflow version contains changes to the workflow interface:

1) You MUST increment the AssemblyVersion attribute in the References.cs file of your workflow project e.g.

from:
assembly: AssemblyVersion("1.1.0.0")]

to:
[assembly: AssemblyVersion("1.2.0.0")]

Don't forget to always update the version number in the workflow.xml file within your VS project to match the new assembly version e.g.

CodeBesideAssembly="MyWF, Version=1.2.0.0, Culture=neutral, PublicKeyToken=c7fd05f7e87052db"

2) When you deploy your new version as a solution the existing assembly gets deleted from the GAC (global assembly cache in c:\windows\assembly) before adding the new one. So:

a) BEFORE you deploy your new version you must take a copy of the existing assembly from the GAC

b) deploy your new version

c) now copy the old assembly back into the GAC (drag and drop in Windows Explorer). You should now have two versions of your assembly in the GAC.

Restart IIS and restart the Windows SharePoint Services Timer service and off you go. You should test that existing workflows work/proceed correctly and that new any new workflow instances also work correctly and exhibit the behaviour desired from the new version.

Remember that you should do some housekeeping in the future to delete any old assembly versions from the GAC that no longer have any dependant workflows.