Skip to main content

Threading in the C# SDK

Threads in a C# program help you achieve parallelism. By using multiple threads, you can make a C# program run faster and do multiple things simultaneously.

The C# SDK supports both single-threading and multi-threading irrespective of a single user or a multi user app.

Refer to the below code snippets that use multi-threading for a single-user and multi-user app.

Multi-threading in a Multi-user App

Multi-threading for multi-users is achieved using Initializer's static SwitchUser().


new Initializer.Builder()
.User(user)
.Environment(environment)
.Token(token)
.SDKConfig(config)
.SwitchUser();
using System;
using System.Collections.Generic;
using System.Threading;
using Com.Zoho.API.Authenticator;
using Com.Zoho.API.Authenticator.Store;
using Com.Zoho.Crm.API;
using Com.Zoho.Crm.API.Dc;
using Com.Zoho.Crm.API.Logger;
using Com.Zoho.Crm.API.Record;
using Com.Zoho.Crm.API.Util;
using Newtonsoft.Json;
using SDKInitializer = Com.Zoho.Crm.API.Initializer;

namespace Com.Zoho.Crm.Sample.Threading.MultiUser
{
    public class MultiThread
    {
        DataCenter.Environment environment;

        UserSignature user;

        Token token;

        string moduleAPIName;

        public MultiThread(UserSignature user, DataCenter.Environment environment, Token token, string moduleAPIName)
        {
            this.environment = environment;

            this.user = user;

            this.token = token;

            this.moduleAPIName = moduleAPIName;
        }

        static void Main(string[] args)
        {
            Logger logger = new Logger.Builder()
            .Level(Logger.Levels.ALL)
            .FilePath("/Users/user_name/Documents/csharp_sdk_log.log")
            .Build();

            DataCenter.Environment environment1 = USDataCenter.PRODUCTION;

            UserSignature user1 = new UserSignature("user1@zoho.com");

            TokenStore tokenstore = new DBStore.Builder()
            .Host("hostName")
            .DatabaseName("dataBaseName")
            .TableName("tableName")
            .UserName("userName")
            .Password("password")
            .PortNumber("portNumber")
            .Build();

            Token token1 = new OAuthToken.Builder()
            //.Id("userID")
            .ClientId("clientId1")
            .ClientSecret("clientSecret1")
            .GrantToken("GRANT token")
            .RefreshToken("REFRESH token")
            .RedirectURL("redirectURL1")
            .Build();

            string resourcePath = "/Users/user_name/Documents/csharpsdk-application";

            SDKConfig config = new SDKConfig.Builder().AutoRefreshFields(true).Build();

            DataCenter.Environment environment2 = EUDataCenter.PRODUCTION;

            UserSignature user2 = new UserSignature("user2@zoho.eu");

            Token token2 = new OAuthToken.Builder()
            //.Id("userID")
            .ClientId("clientId2")
            .ClientSecret("clientSecret2")
            .GrantToken("GRANT token")
            .RefreshToken("REFRESH token")
            .RedirectURL("redirectURL2")
            .Build();

            new SDKInitializer.Builder()
            .User(user1)
            .Environment(environment1)
            .Token(token1)
            .Store(tokenstore)
            .SDKConfig(config)
            .ResourcePath(resourcePath)
            .Logger(logger)
            .Initialize();


            MultiThread multiThread1 = new MultiThread(user1, environment1, token1, "Vendors");

            Thread thread1 = new Thread(() => multiThread1.GetRecords());

            thread1.Start();

            MultiThread multiThread2 = new MultiThread(user2, environment2, token2, "Quotes");

            Thread thread2 = new Thread(() => multiThread2.GetRecords());

            thread2.Start();

            thread1.Join();

            thread2.Join();
        }

        public void GetRecords()
        {
            try
            {
                SDKConfig config = new SDKConfig.Builder().AutoRefreshFields(true).Build();

                new SDKInitializer.Builder()
                .User(this.user)
                .Environment(this.environment)
                .Token(this.token)
                .SDKConfig(config).SwitchUser();

                Console.WriteLine("Fetching records for user - " + SDKInitializer.GetInitializer().User.Email);

                RecordOperations recordOperation = new RecordOperations();

                APIResponse<ResponseHandler> response = recordOperation.GetRecords(this.moduleAPIName, null, null);

                if (response != null)
                {
                    //Get the status code from response
                    Console.WriteLine("Status Code: " + response.StatusCode);

                    if (new List<int>() { 204, 304 }.Contains(response.StatusCode))
                    {
                        Console.WriteLine(response.StatusCode == 204 ? "No Content" : "Not Modified");

                        return;
                    }

                    //Check if expected response is received
                    if (response.IsExpected)
                    {
                        //Get object from response
                        ResponseHandler responseHandler = response.Object;

                        if (responseHandler is ResponseWrapper)
                        {
                            //Get the received ResponseWrapper instance
                            ResponseWrapper responseWrapper = (ResponseWrapper)responseHandler;

                            //Get the list of obtained Record instances
                            List<API.Record.Record> records = responseWrapper.Data;

                            foreach (API.Record.Record record in records)
                            {
                                Console.WriteLine(JsonConvert.SerializeObject(record));
                            }
                        }
                        //Check if the request returned an exception
                        else if (responseHandler is APIException)
                        {
                            //Get the received APIException instance
                            APIException exception = (APIException)responseHandler;

                            //Get the Status
                            Console.WriteLine("Status: " + exception.Status.Value);

                            //Get the Code
                            Console.WriteLine("Code: " + exception.Code.Value);

                            Console.WriteLine("Details: ");

                            //Get the details map
                            foreach (KeyValuePair<string, object> entry in exception.Details)
                            {
                                //Get each value in the map
                                Console.WriteLine(entry.Key + ": " + JsonConvert.SerializeObject(entry.Value));
                            }

                            //Get the Message
                            Console.WriteLine("Message: " + exception.Message.Value);
                        }
                    }
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(JsonConvert.SerializeObject(ex));
            }
        }
    }
}
  • The program execution starts from main().

  • The details of "user1" are given in the variables user1, token1, environment1.

  • Similarly, the details of another user "user2" are given in the variables user2, token2, environment2.

  • For each user, an instance of MultiThread class is created.

  • When start() is called which in-turn invokes run(), the details of user1 are passed to the switchUser function through the MultiThread object. Therefore, this creates a thread for user1.

  • Similarly, When start() is invoked again, the details of user2 are passed to the switchUser function through the MultiThread object. Therefore, this creates a thread for user2.

Multi-threading in a Single-user App


using System;
using System.Collections.Generic;
using System.Threading;
using Com.Zoho.API.Authenticator;
using Com.Zoho.API.Authenticator.Store;
using Com.Zoho.Crm.API;
using Com.Zoho.Crm.API.Dc;
using Com.Zoho.Crm.API.Logger;
using Com.Zoho.Crm.API.Record;
using Com.Zoho.Crm.API.Util;
using Newtonsoft.Json;
using SDKInitializer = Com.Zoho.Crm.API.Initializer;

namespace Com.Zoho.Crm.Sample.Threading.SingleUser
{
    /// <summary>
    ///
    /// </summary>
    public class MultiThread
    {
        static void Main(string[] args)
        {
            Logger logger = new Logger.Builder()
            .Level(Logger.Levels.ALL)
            .FilePath("/Users/user_name/Documents/csharp_sdk_log.log")
            .Build();

            DataCenter.Environment env = USDataCenter.PRODUCTION;

            UserSignature user = new UserSignature("user1@zoho.com");

            TokenStore tokenstore = new FileStore("/Users/user_name/Documents/csharp_sdk_token.txt");

            Token token = new OAuthToken.Builder()
            //.Id("userID")
            .ClientId("clientId")
            .ClientSecret("clientSecret1")
            .GrantToken("GRANT token")
            .RefreshToken("REFRESH token")
            .RedirectURL("redirectURL1")
            .Build();

            string resourcePath = "/Users/user_name/Documents/csharpsdk-application";

            SDKConfig config = new SDKConfig.Builder().AutoRefreshFields(true).Build();

            new SDKInitializer.Builder()
                .User(user)
                .Environment(env)
                .Token(token)
                .Store(tokenstore)
                .SDKConfig(config)
                .ResourcePath(resourcePath)
                .Logger(logger)
                .Initialize();
            MultiThread multiThread1 = new MultiThread();

            Thread thread1 = new Thread(() => multiThread1.GetRecords("Quotes"));

            thread1.Start();

            Thread thread2 = new Thread(() => multiThread1.GetContactRoles());

            thread2.Start();

            thread1.Join();

            thread2.Join();
        }

        public void GetRecords(string moduleAPIName)
        {
            try
            {
                Console.WriteLine("Fetching records for user - " + SDKInitializer.GetInitializer().User.Email);

                RecordOperations recordOperation = new RecordOperations();

                APIResponse<API.Record.ResponseHandler> response = recordOperation.GetRecords(moduleAPIName, null, null);

                if (response != null)
                {
                    //Get the status code from response
                    Console.WriteLine("Status Code: " + response.StatusCode);

                    if (new List<int>() { 204, 304 }.Contains(response.StatusCode))
                    {
                        Console.WriteLine(response.StatusCode == 204 ? "No Content" : "Not Modified");

                        return;
                    }

                    //Check if expected response is received
                    if (response.IsExpected)
                    {
                        //Get object from response
                        API.Record.ResponseHandler responseHandler = response.Object;

                        if (responseHandler is API.Record.ResponseWrapper)
                        {
                            //Get the received ResponseWrapper instance
                            API.Record.ResponseWrapper responseWrapper = (API.Record.ResponseWrapper)responseHandler;

                            //Get the list of obtained Record instances
                            List<API.Record.Record> records = responseWrapper.Data;

                            foreach (API.Record.Record record in records)
                            {
                                Console.WriteLine(JsonConvert.SerializeObject(record));
                            }
                        }
                        //Check if the request returned an exception
                        else if (responseHandler is API.Record.APIException)
                        {
                            //Get the received APIException instance
                            APIException exception = (APIException)responseHandler;

                            //Get the Status
                            Console.WriteLine("Status: " + exception.Status.Value);

                            //Get the Code
                            Console.WriteLine("Code: " + exception.Code.Value);

                            Console.WriteLine("Details: ");

                            //Get the details map
                            foreach (KeyValuePair<string, object> entry in exception.Details)
                            {
                                //Get each value in the map
                                Console.WriteLine(entry.Key + ": " + JsonConvert.SerializeObject(entry.Value));
                            }

                            //Get the Message
                            Console.WriteLine("Message: " + exception.Message.Value);
                        }
                    }
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(JsonConvert.SerializeObject(ex));
            }
        }

        public void GetContactRoles()
        {
            try
            {
                Console.WriteLine("Fetching Cr's for user - " + SDKInitializer.GetInitializer().User.Email);

                ContactRolesOperations contactRolesOperations = new ContactRolesOperations();

                APIResponse<ResponseHandler> response = contactRolesOperations.GetContactRoles();

                if (response != null)
                {
                    //Get the status code from response
                    Console.WriteLine("Status Code: " + response.StatusCode);

                    if (new List<int>() { 204, 304 }.Contains(response.StatusCode))
                    {
                        Console.WriteLine(response.StatusCode == 204 ? "No Content" : "Not Modified");

                        return;
                    }

                    //Check if expected response is received
                    if (response.IsExpected)
                    {
                        //Get object from response
                        ResponseHandler responseHandler = response.Object;

                        if (responseHandler is ResponseWrapper)
                        {
                            //Get the received ResponseWrapper instance
                            ResponseWrapper responseWrapper = (ResponseWrapper)responseHandler;

                            //Get the list of obtained Record instances
                            List<ContactRole> contactRoles = responseWrapper.ContactRoles;

                            foreach (ContactRole contactRole in contactRoles)
                            {
                                Console.WriteLine(JsonConvert.SerializeObject(contactRole));
                            }
                        }
                        //Check if the request returned an exception
                        else if (responseHandler is APIException)
                        {
                            //Get the received APIException instance
                            APIException exception = (APIException)responseHandler;

                            //Get the Status
                            Console.WriteLine("Status: " + exception.Status.Value);

                            //Get the Code
                            Console.WriteLine("Code: " + exception.Code.Value);

                            Console.WriteLine("Details: ");

                            //Get the details map
                            foreach (KeyValuePair<string, object> entry in exception.Details)
                            {
                                //Get each value in the map
                                Console.WriteLine(entry.Key + ": " + JsonConvert.SerializeObject(entry.Value));
                            }

                            //Get the Message
                            Console.WriteLine("Message: " + exception.Message.Value);
                        }
                    }
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(JsonConvert.SerializeObject(ex));
            }
        }
    }
}
  • The program execution starts from Main() where the SDK is initialized with the details of user and an instance of MultiThread class is created .
  • When the Start() is called which in-turn invokes the run(), the moduleAPIName is switched through the method parameter. Therefore, this creates a thread for the particular method called with the MultiThread instance.

SDK Sample Code


using System;
using System.Collections.Generic;
using Com.Zoho.API.Authenticator;
using Com.Zoho.API.Authenticator.Store;
using Com.Zoho.Crm.API;
using Com.Zoho.Crm.API.Dc;
using Com.Zoho.Crm.API.Logger;
using Com.Zoho.Crm.API.Record;
using Com.Zoho.Crm.API.Tags;
using Com.Zoho.Crm.API.Users;
using Com.Zoho.Crm.API.Util;
using Newtonsoft.Json;
using static Com.Zoho.API.Authenticator.OAuthToken;
using static Com.Zoho.Crm.API.Record.RecordOperations;
using Environment = Com.Zoho.Crm.API.Dc.DataCenter.Environment;
using ResponseHandler = Com.Zoho.Crm.API.Record.ResponseHandler;
using ResponseWrapper = Com.Zoho.Crm.API.Record.ResponseWrapper;
using SDKInitializer = Com.Zoho.Crm.API.Initializer;

namespace TestAutomatedSDK
{
    public class MainClass
    {

        public static void Main(string[] args)
        {
            /*
             * Create an instance of Logger Class that takes two parameters
             * Level -> Level of the log messages to be logged. Can be configured by typing Levels "." and choose any level from the list displayed.
             * FilePath -> Absolute file path, where messages need to be logged.
             */
            Logger logger = new Logger.Builder()
            .Level(Logger.Levels.ALL)
            .FilePath("/Users/user_name/Documents/csharp_sdk_log.log")
            .Build();

            //Create an UserSignature instance that takes user Email as parameter
            UserSignature user = new UserSignature("abc@zoho.com");

            /*
             * Configure the environment
             * which is of the pattern Domain.Environment
             * Available Domains: USDataCenter, EUDataCenter, INDataCenter, CNDataCenter, AUDataCenter
             * Available Environments: PRODUCTION, DEVELOPER, SANDBOX
             */
            Environment environment = USDataCenter.PRODUCTION;

            /*
             * Create a Token instance
             */
            Token token = new OAuthToken.Builder()
            //.Id("userID")
            .ClientId("clientId")
            .ClientSecret("clientSecret")
            .GrantToken("GRANT token")
            .RefreshToken("REFRESH token")
            .RedirectURL("redirectURL")
            .Build();

            /*
             * Create an instance of TokenStore.
             */
            TokenStore tokenstore = new DBStore.Builder()
            .Host("hostName")
            .DatabaseName("dataBaseName")
            .TableName("tableName")
            .UserName("userName")
            .Password("password")
            .PortNumber("portNumber")
            .Build();

            //TokenStore tokenstore = new FileStore("absolute_file_path");

            /*
            * autoRefreshFields
            * if true - all the modules' fields will be auto-refreshed in the background, every    hour.
            * if false - the fields will not be auto-refreshed in the background. The user can manually delete the file(s) or refresh the fields using methods from ModuleFieldsHandler(com.zoho.crm.api.util.ModuleFieldsHandler)
            *
            * pickListValidation
            * if true - value for any picklist field will be validated with the available values.
            * if false - value for any picklist field will not be validated, resulting in creation of a new value.
            */

            SDKConfig config = new SDKConfig.Builder().AutoRefreshFields(true).PickListValidation(false).Build();

            string resourcePath = "/Users/username/Documents/csharpsdk-application";

            /*
            * Call static initialize method of Initializer class that takes the arguments
            * User -> UserSignature instance
            * Environment -> Environment instance
            * Token -> Token instance
            * Store -> TokenStore instance
            * SDKConfig -> SDKConfig instance
            * ResourcePath -> resourcePath - A String
            * Logger -> Logger instance
            */
            new SDKInitializer.Builder()
            .User(user)
            .Environment(environment)
            .Token(token)
            .Store(tokenstore)
            .SDKConfig(config)
            .ResourcePath(resourcePath)
            .Logger(logger)
            .Initialize();

            String moduleAPIName = "Leads";

            RecordOperations recordOperations = new RecordOperations();

            ParameterMap paramInstance = new ParameterMap();

            paramInstance.Add(GetRecordsParam.APPROVED, "both");

            HeaderMap headerInstance = new HeaderMap();

            DateTimeOffset ifmodifiedsince = new DateTimeOffset(new DateTime(2020, 05, 15, 12, 0, 0, DateTimeKind.Local));

            headerInstance.Add(GetRecordsHeader.IF_MODIFIED_SINCE, ifmodifiedsince);

            //Call getRecords method
            APIResponse<ResponseHandler> response = recordOperations.GetRecords(moduleAPIName, paramInstance, headerInstance);

            if (response != null)
            {
                //Get the status code from response
                Console.WriteLine("Status Code: " + response.StatusCode);

                if (new List<int>() { 204, 304 }.Contains(response.StatusCode))
                {
                    Console.WriteLine(response.StatusCode == 204 ? "No Content" : "Not Modified");

                    return;
                }

                //Check if expected response is received
                if (response.IsExpected)
                {
                    //Get the object from response
                    ResponseHandler responseHandler = response.Object;

                    if (responseHandler is ResponseWrapper)
                    {
                        //Get the received ResponseWrapper instance
                        ResponseWrapper responseWrapper = (ResponseWrapper)responseHandler;

                        //Get the obtained Record instances
                        List<Com.Zoho.Crm.API.Record.Record> records = responseWrapper.Data;

                        foreach (Com.Zoho.Crm.API.Record.Record record in records)
                        {
                            //Get the ID of each Record
                            Console.WriteLine("Record ID: " + record.Id);

                            //Get the createdBy User instance of each Record
                            User createdBy = record.CreatedBy;

                            //Check if createdBy is not null
                            if (createdBy != null)
                            {
                                //Get the ID of the createdBy User
                                Console.WriteLine("Record Created By User-ID: " + createdBy.Id);

                                //Get the name of the createdBy User
                                Console.WriteLine("Record Created By User-Name: " + createdBy.Name);

                                //Get the Email of the createdBy User
                                Console.WriteLine("Record Created By User-Email: " + createdBy.Email);
                            }

                            //Get the CreatedTime of each Record
                            Console.WriteLine("Record CreatedTime: " + record.CreatedTime);

                            //Get the modifiedBy User instance of each Record
                            User modifiedBy = record.ModifiedBy;

                            //Check if modifiedBy is not null
                            if (modifiedBy != null)
                            {
                                //Get the ID of the modifiedBy User
                                Console.WriteLine("Record Modified By User-ID: " + modifiedBy.Id);

                                //Get the name of the modifiedBy User
                                Console.WriteLine("Record Modified By User-Name: " + modifiedBy.Name);

                                //Get the Email of the modifiedBy User
                                Console.WriteLine("Record Modified By User-Email: " + modifiedBy.Email);
                            }

                            //Get the ModifiedTime of each Record
                            Console.WriteLine("Record ModifiedTime: " + record.ModifiedTime);

                            //Get the list of Tag instance each Record
                            List<Tag> tags = record.Tag;

                            //Check if tags is not null
                            if (tags != null)
                            {
                                foreach (Tag tag in tags)
                                {
                                    //Get the Name of each Tag
                                    Console.WriteLine("Record Tag Name: " + tag.Name);

                                    //Get the Id of each Tag
                                    Console.WriteLine("Record Tag ID: " + tag.Id);
                                }
                            }

                            //To get particular field value
                            Console.WriteLine("Record Field Value: " + record.GetKeyValue("Last_Name"));// FieldApiName

                            Console.WriteLine("Record KeyValues: ");

                            //Get the KeyValue map
                            foreach (KeyValuePair<string, object> entry in record.GetKeyValues())
                            {
                                string keyName = entry.Key;

                                object value = entry.Value;

                                if (value != null)
                                {
                                    Console.WriteLine("Field APIName : " + keyName + "\tValue : " + JsonConvert.SerializeObject(value));
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}