Starting a Workflow with CSOM

6 minute read

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);