Monday, May 27, 2013

Programmatically Start SharePoint 2013 Workflow

SharePoint 2013 introduced a new set of API to work with new workflow model. In my previous post I described how to access workflows associated with a list/list item and access workflow properties. In this post I’ll describe how to run SharePoint 2013 workflow programmatically.

First you need to create instance of WorkflowServicesManager, then you need to find out the subscription (which actually represent the workflow association). Finally you can run the workflow with WorkflowInstance Service as shown below:

var workflowServiceManager = new WorkflowServicesManager(web);
var workflowSubscriptionService = workflowServiceManager.GetWorkflowSubscriptionService();

//get all workflows associated with the list
var subscriptions = workflowSubscriptionService.EnumerateSubscriptionsByList(listId);

//run all workflows associated with the list
foreach (var workflowSubscription in subscriptions)
{
    //initiation parameters
    var inputParameters = new Dictionary<string, object>();
    inputParameters.Add("MyProperty", "MyValue");

    workflowServiceManager.GetWorkflowInstanceService().StartWorkflowOnListItem(workflowSubscription, itemId, inputParameters);
}

In the StartWorkflowOnListItem, if you have any input parameters (which you usually pass from workflow initiation form), you can pass the input paramters as a dictionary. Otherwise, you can pass an empty dictionary.

 

Similarly you can terminate or suspend workflows using Workflow Instance Service.

For your information, workflow will not run if you use an elevated web in WorkflowServicesManager. You need to make sure you are running the code with an user who is not system account and who has a user profile.

14 comments:

  1. Hello Sohel.
    Thank you very much for sharing the information related to SP2013 workflows and Workflow Manager. As I can see you are one of not so many people who try to make their own investigation and share knowledge of the new WF platform to help others to overcome the lack of documentation. Thank you for that!

    I am trying to understand how to work with this technology in deep and have a question that you probably know the answer for. Do you know whether it is possible to get value of SPD WF variable from C# code? I tried to do that with Microsoft.Workflow.Client and Microsoft.SharePoint.Client.WorkflowServices namespaces, but with no result. Do you have any ideas?

    ReplyDelete
  2. As far as I know, you can access workflow variables only inside workflow. However you can pass your variable to external WCF service. So if you need to access workflow variables in C# code then pass the variables in the service as the new WF model doesn't allow any C# code in normal scenario.

    ReplyDelete
  3. Hi Sohel!
    How to work with "inputParameters" dictionary values inside workflow? I mean how to get these values into workflow variables in Visual Studio?
    I tried to create Argument with the same name as key name. Got an error at workflow feature activating. And I tried to use ExternalVariableValue activity - also got an error.
    Please help with my question.
    Thanks.

    ReplyDelete
  4. @Vladimir, You need to create Argument with direction 'In'. Please take a look at the example: http://code.msdn.microsoft.com/office/SharePoint-2013-Approval-f5ac5eb2/

    ReplyDelete
  5. Hi Sohel, thanks for sharing your knowledge in SP2013 workflows which are very helpful to me. I am using a solution similar to yours to start a workflow on a list item. The problem I encountered is that I want to redirect the user to the Initiation Form page if the Initiation Form Parameters have been defined for the workflow, and skip this step (just start the workflow) if no parameters have been defined. However I cannot find a property in the new API that can help me determine if the parameters have been defined or not. For SP2010 workflows, I can check the InstantiationUrl property of the SPWorkflowAssociation object to determine this, but I have not found a similar way to achieve this for SP2013 workflows.

    Any suggestion is greatly appreciated!

    Thanks.

    ReplyDelete
  6. @mayflower, you need to retrieve the workflow definition by using 'WorkflowServiceManager.GetWorkflowDeploymentService().GetDefinition(workflowSubscription.DefinitionId)'. Then in the workflow definition you will find the properties as well as initiation/association form url.

    ReplyDelete
  7. We are not able to kick off workflow for read access users. We have to give contribute rights to those users. We tried token code but its not working.
    Do you know any fix

    ReplyDelete
  8. Thanks for the post but i always getting asp.net access denied when trying to start workflow...
    i don't have problem to access & update list items.The web is running with custom user who has rights to SharePoint site and lists (owner)
    thanks

    ReplyDelete
  9. How to load he initiation form, when called the workflow instance programmatically through event receiver or something?

    ReplyDelete
  10. @SP Dev, not a good idea of showing initiation form from even receiver. If it's sync receiver then you can redirect the user to workflow initiation form. Otherwise you can initiate the workflow programmatically passing the initiation data.

    ReplyDelete
  11. sohel how to run sharepoint 2013 designer workflow from application page programmatically . there are two workflow Getapproval & SendMail . i want to start these workflow from c# code.
    Please help me me.

    ReplyDelete
  12. In a SharePoint online, I created an app that provision a site and then initiate approval workflow. The problem I have is that, the app don't have enough permission to start the workflow. After the site is created, I have to set the permission by going to _layouts/15/appinv.aspx page the kick the workflow.

    the Workflows can use app permissions feature is already activated on the site.

    The question I have is, how can an app that created the site, start the workflow?

    ReplyDelete
  13. I can not get pass the WorkFlowSubscriptionService ServerObjectIsNull == true:

    public static void StartWorkflow(String Tier3TORSite, String userName)
    {
    ClientContext ctx = new ClientContext(Tier3TORSite) { Credentials = CredentialCache.DefaultCredentials };
    Web web = ctx .Web;
    List documents = ctx.Web.Lists.GetByTitle("Time Off Requests");
    ctx .Load(documents);
    ctx .ExecuteQuery();
    WorkflowServicesManager wfManager = new WorkflowServicesManager(ctx, web);
    WorkflowInstanceService instanceService = wfManager.GetWorkflowInstanceService();
    ListItemCollection items = documents.GetItems(CamlQuery.CreateAllItemsQuery());
    ctx.Load(items);
    ctx.ExecuteQuery();
    foreach (ListItem item in items)
    {
    var args1 = new Dictionary();
    args1.Add("WorkflowHistory", "Hello");

    // get reference to Workflow Service Manager (WSM) in SP...
    WorkflowServicesManager wsm = new WorkflowServicesManager(ctx, web);

    ctx.Load(wsm);
    ctx.ExecuteQuery();
    wsm.ServerObjectIsNull.Dump("WorkflowServicesManager: ServerObjectIsNull");
    if (wsm.ServerObjectIsNull == true)
    {
    return;
    }
    else
    {
    // get reference to subscription service
    WorkflowSubscriptionService subscriptionService = wsm.GetWorkflowSubscriptionService();
    ctx.Load(subscriptionService);
    ctx.ExecuteQuery();
    subscriptionService.ServerObjectIsNull.Dump("WorkflowSubscriptionService: ServerObjectIsNull");
    if (subscriptionService.ServerObjectIsNull == true)
    {
    return;
    }
    else
    {
    var subscriptions = subscriptionService.EnumerateSubscriptionsByList(documents.Id);

    ctx.Load(subscriptions);
    ctx.ExecuteQuery();
    instanceService.StartWorkflowOnListItem(subscriptions[0], item.Id, args1);
    ctx.ExecuteQuery();
    }
    }
    }
    }

    ReplyDelete
    Replies
    1. Note: The Dump()... is from LINQPad 4. they show:

      WorkflowServicesManager: ServerObjectIsNull = False
      WorkflowSubscriptionService: ServerObjectIsNull = True

      Delete