Posts Tagged ‘workflow’

SharePoint workflow + list item edit + value cannot be null

Thursday, May 26th, 2011

You may encounter a random error when using a custom Visual Studio SharePoint 2010 workflow. Everything appears OK, but when you go to edit the item that the workflow is running on, the EditForm fails to load and you see an error like:

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: s

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[ArgumentNullException: Value cannot be null.
Parameter name: s]
System.IO.StringReader..ctor(String s) +10151478
System.Xml.XmlDocument.LoadXml(String xml) +51
Microsoft.SharePoint.Publishing.Internal.WorkflowUtilities.FlattenXmlToHashtable(String strXml) +90

(more…)

SharePoint 2010 + Associate workflow to Content Type in a feature

Thursday, May 5th, 2011

Quick tip: this plagued me for a while. You may develop a custom workflow which should only be associated with particular content types. You can deploy it to your site and then manually associate it to your content type. It’s possible to do this in code but the process isn’t as straight forward as it should be. Not to mention the fact that it’s infuriatingly hard to debug EventReceivers (“no symbols have been loaded” misery!).

Here are some steps to try/follow:

1) Creating the workflow. (Duh.)
2) Add a feature to your project.
3) Add an Event Receiver to your feature, e.g. Feature1.EventReceiver.cs
4) Uncomment the two blocks for FeatureActivated and FeatureDeactivating
5) Leave the GUID intact.
6) Your FeatureActivated code might look like this:

Note: this assume the Feature is deployed at the Site Collection level. If you’re at a different level, update the casting on properties.feature.Parent

  private string WorkflowName = "My Great Workflow";
        private string ContentTypeName = "My Great Content Type";

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            using (SPSite site = ((SPSite)properties.Feature.Parent))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    SPContentType theCT = web.ContentTypes[ContentTypeName];

                    SPWorkflowTemplate theWF = null;
                    foreach (SPWorkflowTemplate tpl in web.WorkflowTemplates)
                    {
                        if (tpl.Name == WorkflowName)
                        {
                            theWF = tpl;
                        }
                    }

                    SPWorkflowAssociation wfAssociation = theCT.WorkflowAssociations.GetAssociationByName(WorkflowName, web.Locale);
                    if (wfAssociation != null)
                    {
                        theCT.WorkflowAssociations.Remove(wfAssociation);
                    }
                    theCT.UpdateWorkflowAssociationsOnChildren(true, true, true, false);

                    wfAssociation = SPWorkflowAssociation.CreateWebContentTypeAssociation(theWF, WorkflowName, "New Business Tasks", "New Business History");

                    if (theCT.WorkflowAssociations.GetAssociationByName(wfAssociation.Name, web.Locale) == null)
                    {
                        theCT.WorkflowAssociations.Add(wfAssociation);
                    }
                    else
                    {
                        theCT.WorkflowAssociations.Update(wfAssociation);
                    }
                    theCT.UpdateWorkflowAssociationsOnChildren(true, true, true, false);
                }
            }

7) Your FeatureDeactivating code might look like this:

 public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            using (SPSite site = ((SPSite)properties.Feature.Parent))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    SPContentType theCT = web.ContentTypes[ContentTypeName];

                    SPWorkflowTemplate theWF = null;
                    foreach (SPWorkflowTemplate tpl in web.WorkflowTemplates)
                    {
                        if (tpl.Name == WorkflowName)
                        {
                            theWF = tpl;
                        }
                    }

                    SPWorkflowAssociation wfAssociation = theCT.WorkflowAssociations.GetAssociationByName(WorkflowName, web.Locale);
                    if (wfAssociation != null)
                    {
                        theCT.WorkflowAssociations.Remove(wfAssociation);
                    }
                    theCT.UpdateWorkflowAssociationsOnChildren(true, true, true, false);
                }
            }
        }

8) This is the key bit! In your Feature1.template.xml file, ensure you set the ReceiverAssembly and ReceiverClass parts correctly. That is: the ReceiverAssembly is the compiled assembly of your e.g., workflow You can check this by browsing to the GAC (e.g., C:\Windows\assembly) and checking the version/publickeytoken, etc. The ReceiverClass is the entry point to your EventReceiver. So if the namespace in your Feature1.EventReceiver.cs file is MyNameSpace.MyGreatProject.Features.Feature1 and the class name was Feature1EventReceiver, and this was all compiled in to an assembly called MyGreatProject (v1.0.0.0) then your Feature.xml might look like:

<Feature ReceiverAssembly="MyGreatProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b3d25ed43b57ce9e"
ReceiverClass="NyNameSpace.MyGreatProject.Features.Feature1.Feature1EventReceiver">
<Properties>
<Property Key="GloballyAvailable" Value="true"/>
<Property Key="RegisterForms" Value="*.xsn" />
</Properties>
</Feature>

Deploy and all should be OK.

SharePoint + Visual Studio + Get Current User

Wednesday, May 4th, 2011

A frequently asked question in the MSDN forums is “how you can get access to the user who is interacting with your workflow?”. For example, the user modifying a task. The workflow is likely running in a different context and/or session to your browser session so there’s not an obvious tie-up.

However, in this scenario, you can get the login name of the person who modified the SharePoint task, via the Executor property of the OnTaskChanged event. Simply bind the Executor property to a string (e.g., “taskLastModifiedBy”) and whenever the task changes, SharePoint will copy the user ID to this property in the format of DOMAIN\\LoginName. You can then get an SPUser object for that login name with, e.g.

SPUser user = workflowProperties.Web.AllUsers[taskLastModifiedBy];

Note: On a related subject, and the thing that prompted this post – if you’re trying to update a Person or Group field on your workflowProperties.Item, then you must pass it an SPUser object! This is bizarre, because other types of list (e.g, the task list) you can pass it a string and SharePoint will do the rest. I spent ages and all kinds of different things and always getting the “Invalid data has been used to update the list item. The field you are trying to update may be read only.” error. Annoying.

(more…)

Visual Studio 2010 Workflow + Infopath 2010 forms – troubleshooting

Monday, March 28th, 2011

Microsoft markets that Sharepoint Designer workflows are directly exportable to Visual Studio workflows. And in many ways, this is absolutely true. It’s trivial to create a .wsp from an SPD workflow and import it to VS. However, if you then inspect the workflow that has been created for you, a little concern would not be underestimated. If your SPD workflow was what you wanted, then you could be reasonably confident that its VS version would do the same job. But presumably your reason for importing to VS is because you need more control over the workflow in the longer term, in which case, you’re likely to be left underwhelmed by what the import process creates for you. Imagine creating a webpage in Microsoft Word and you’ll get the drift.

So when faced with a similar proposition, recreating the workflow in Visual Studio from scratch immediately became apparent as the best solution. After all, the intent and purpose of the workflow was clear; recreating it in VS ought to have been a doddle. However, to complicate matters, the SPD workflow used Infopath edit forms. This turned out to be easy in SPD, but non-trivial in Visual Studio. Getting an Infopath form to register with a VS workflow project requires various items, that VS may not do automatically for you.

(more…)

Set SharePoint BDC field value programmatically in Visual Studio workflow

Wednesday, June 16th, 2010

The Business Data Catalog (BDC) in SharePoint is a superb way of hooking up virtually any disparate system to SharePoint 2007. By defining the connection in to the system, you can expose any part of the system to SharePoint, and harness some of SharePoint’s real power – for example, its powerful search capabilities. Once you’ve created your connection (your Line Of Business [LOB] system), you can attach BDC columns to any list (e.g., document library) and you can, for example, have a document with a reference to your other system, so that you can permanently associate extra data to your document, without having to reproduce the information in SharePoint and create unnecessary duplication.

Using the BDC is beyond the scope of this post, but I’d like to cover an issue that I recently encountered when trying to work with the BDC and a document library. I had a fairly simple Visual Studio 2008 workflow that ran when a new item was created in the document library.

(more…)