Starting a Workflow with CSOM

There are situations were you would like to start a workflow by using  code. In one of my last projects there was that kind of situation, in that project we needed to start a workflow on every item in the SharePoint list. In the past I had done this kind of work many times with the server object model but since this was a SharePoint Online instance I was not able to use the server object model.

As you are unable to use the server object model you need to use the Client Side Object Model (CSOM).  In the upcoming article I will walk you trough the code step by step. Make sure that before you start that you add the OfficeDevPpPCore16 nuget package to your application.

 

First thing you need to do when using the client side object model is getting the client context for the site. The URL that you use for the context will need to be the absolute URL of the site on which you want to start the Workflow.  In order to build the client context you need the login name of the user, his password and the site URL.

 

Console.WriteLine("Enter the Office 365 Login Name");
string loginId = Console.ReadLine();
string pwd = GetInput("Password", true);

Console.WriteLine("Web Url:");
string webUrl = Console.ReadLine();

Console.WriteLine("List Name:");
string listName = Console.ReadLine();

Console.WriteLine("Workflow Name");
string workflowName = Console.ReadLine();

var passWord = new SecureString();
foreach (char c in pwd.ToCharArray()) passWord.AppendChar(c);

using (var ctx = new ClientContext(webUrl)) {
    ctx.Credentials = new SharePointOnlineCredentials(loginId, passWord);
}


In the above code the is a reference to a method called “GetInput” We use this method for getting the password of the user without seeing what the user is typing in the console window.

 

private static string GetInput(string label, bool isPassword) {
    Console.ForegroundColor = ConsoleColor.White;
    Console.Write("{0} : ", label);
    Console.ForegroundColor = ConsoleColor.Gray;

    string strPwd = "";

    for (ConsoleKeyInfo keyInfo = Console.ReadKey(true); keyInfo.Key != ConsoleKey.Enter; keyInfo = Console.ReadKey(true)) {
        if (keyInfo.Key == ConsoleKey.Backspace) {
            if (strPwd.Length > 0) {
                strPwd = strPwd.Remove(strPwd.Length - 1);
                Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                Console.Write(" ");
                Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
            }
        } else if (keyInfo.Key != ConsoleKey.Enter) {
            if (isPassword) {
                Console.Write("*");
            } else {
                Console.Write(keyInfo.KeyChar);
            }
            strPwd += keyInfo.KeyChar;

        }

    }
    Console.WriteLine("");

    return strPwd;
}

When the client context is setup we can start with the actions we need to perform. The first action we will perform is getting a reference to all required workflow components.components

var workflowServicesManager = new WorkflowServicesManager(ctx, ctx.Web);
var workflowInteropService = workflowServicesManager.GetWorkflowInteropService();
var workflowSubscriptionService = workflowServicesManager.GetWorkflowSubscriptionService();
var workflowDeploymentService = workflowServicesManager.GetWorkflowDeploymentService();
var workflowInstanceService = workflowServicesManager.GetWorkflowInstanceService();

 

This above components are all needed in order to start a workflow. The component we will use first is the deployment service, this service will be used to get all published workflow definitions on the site.

var publishedWorkflowDefinitions = workflowDeploymentService.EnumerateDefinitions(true);
ctx.Load(publishedWorkflowDefinitions);
ctx.ExecuteQuery();

var def = from defs in publishedWorkflowDefinitions
          where defs.DisplayName == workflowName
          select defs;

WorkflowDefinition workflow = def.FirstOrDefault();

if(workflow != null) {

}

As you can see in the above code the workflow definitions are retrieved from the site. In this collection we try to find our workflow definition we need by using the workflow name.

 

// get all workflow associations
var workflowAssociations = workflowSubscriptionService.EnumerateSubscriptionsByDefinition(workflow.Id);
ctx.Load(workflowAssociations);
ctx.ExecuteQuery();

// find the first association
var firstWorkflowAssociation = workflowAssociations.First();

// start the workflow
var startParameters = new Dictionary<string, object>();

if (ctx.Web.ListExists(listName)) {
    List list = ctx.Web.GetListByTitle(listName);

    CamlQuery query = CamlQuery.CreateAllItemsQuery();
    ListItemCollection items = list.GetItems(query);

    // Retrieve all items in the ListItemCollection from List.GetItems(Query).
    ctx.Load(items);
    ctx.ExecuteQuery();
    foreach (ListItem listItem in items) {
        Console.WriteLine("Starting workflow for item: " + listItem.Id);
        workflowInstanceService.StartWorkflowOnListItem(firstWorkflowAssociation, listItem.Id, startParameters);
        ctx.ExecuteQuery();
    }
}

When the correct definition is loaded we need to association  to be able to start the workflow. Together with the association you also need start parameters to start the workflow, because in our situation we do not have any start parameters we pass in a empty dictionary.  If the list exists we create a CAML query to retrieve all the items in the list.

As last step we can iterate trough the list items and start the workflow for each item by calling the “StartWorkflowOnListItem” method.

 

Below you can find the complete reference for the source code.

 

using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.WorkflowServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace CSOMStartWorkflow {
    class Program {
        static void Main(string[] args) {

            Console.WriteLine("Enter the Office 365 Login Name");
            string loginId = Console.ReadLine();
            string pwd = GetInput("Password", true);

            Console.WriteLine("Web Url:");
            string webUrl = Console.ReadLine();

            Console.WriteLine("List Name:");
            string listName = Console.ReadLine();

            Console.WriteLine("Workflow Name");
            string workflowName = Console.ReadLine();

            var passWord = new SecureString();
            foreach (char c in pwd.ToCharArray()) passWord.AppendChar(c);

            using (var ctx = new ClientContext(webUrl)) {
                ctx.Credentials = new SharePointOnlineCredentials(loginId, passWord);

                var workflowServicesManager = new WorkflowServicesManager(ctx, ctx.Web);
                var workflowInteropService = workflowServicesManager.GetWorkflowInteropService();
                var workflowSubscriptionService = workflowServicesManager.GetWorkflowSubscriptionService();
                var workflowDeploymentService = workflowServicesManager.GetWorkflowDeploymentService();
                var workflowInstanceService = workflowServicesManager.GetWorkflowInstanceService();

                var publishedWorkflowDefinitions = workflowDeploymentService.EnumerateDefinitions(true);
                ctx.Load(publishedWorkflowDefinitions);
                ctx.ExecuteQuery();

                var def = from defs in publishedWorkflowDefinitions
                          where defs.DisplayName == workflowName
                          select defs;

                WorkflowDefinition workflow = def.FirstOrDefault();

                if(workflow != null) {


                    // get all workflow associations
                    var workflowAssociations = workflowSubscriptionService.EnumerateSubscriptionsByDefinition(workflow.Id);
                    ctx.Load(workflowAssociations);
                    ctx.ExecuteQuery();

                    // find the first association
                    var firstWorkflowAssociation = workflowAssociations.First();
                    
                    // start the workflow
                    var startParameters = new Dictionary<string, object>();

                    if (ctx.Web.ListExists(listName)) {
                        List list = ctx.Web.GetListByTitle(listName);

                        CamlQuery query = CamlQuery.CreateAllItemsQuery();
                        ListItemCollection items = list.GetItems(query);

                        // Retrieve all items in the ListItemCollection from List.GetItems(Query).
                        ctx.Load(items);
                        ctx.ExecuteQuery();
                        foreach (ListItem listItem in items) {
                            Console.WriteLine("Starting workflow for item: " + listItem.Id);
                            workflowInstanceService.StartWorkflowOnListItem(firstWorkflowAssociation, listItem.Id, startParameters);
                            ctx.ExecuteQuery();
                        }
                    }
                }
            }

            Console.WriteLine("Press any key to close....");
            Console.ReadKey();
        }

        private static string GetInput(string label, bool isPassword) {
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write("{0} : ", label);
            Console.ForegroundColor = ConsoleColor.Gray;

            string strPwd = "";

            for (ConsoleKeyInfo keyInfo = Console.ReadKey(true); keyInfo.Key != ConsoleKey.Enter; keyInfo = Console.ReadKey(true)) {
                if (keyInfo.Key == ConsoleKey.Backspace) {
                    if (strPwd.Length > 0) {
                        strPwd = strPwd.Remove(strPwd.Length - 1);
                        Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                        Console.Write(" ");
                        Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                    }
                } else if (keyInfo.Key != ConsoleKey.Enter) {
                    if (isPassword) {
                        Console.Write("*");
                    } else {
                        Console.Write(keyInfo.KeyChar);
                    }
                    strPwd += keyInfo.KeyChar;

                }

            }
            Console.WriteLine("");

            return strPwd;
        }
    }
}

The complete solution is also posted on: https://code.msdn.microsoft.com/ direct link to the sample:

https://code.msdn.microsoft.com/Starting-a-SharePoint-9bcf7c5d

 

Related Posts

Forcing a Device Channel Device channels are a way of using different master pages for different devices. MSDN describes the functionality as followed:   “Browsing ...
Host-Named Site Collections (Provisioning by Code and in Specific Content Databa... Host-named site collections are the preferred method to deploy sites in SharePoint 2013. Because the Office 365 environment uses host-named site coll...
Retrieve the Friendly URL of a Publishing Page One of my first projects with SharePoint 2013 is building a SharePoint website. For the website we had to build a functionality that display’s the URL...

11 comments

  • Thank you for this. Howevere, I had trouble on:

    ctx.Web.ListExists(listName)

    …and:

    List list = ctx.Web.GetListByTitle(listName);

    Otherwise, perfect working code for me.

  • Hi ,

    i am not able to get the SP2010 designer workflow details in publishedWorkflowDefinitions. Please help me.

    thanks,
    Santhosh

  • Hello will this code run in the case, if i have created SharePoint 2010 template workflow in Office 365.????

    I have created SharePoint 2010 workflow, I am able to kickoff manually but when I try to kickoff from CSOM code mentioned above, I am not able find workflow, I always get null value in following snippet when I try to find SharePoint 2010 workflow. However I am able to view all other workflow than 2010 SPD workflows.

    WorkflowDefinition workflow = def.FirstOrDefault();
    if(workflow != null) {
    }

  • Hi

    Please help how would you retrieve workflows associated to a specific item then pop up an interface to allow the user to run a specific workflow rather than starting all workflow?

Leave a Reply

Your email address will not be published. Required fields are marked *