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 SDKInitializer.Builder()
.User(user)
.Environment(environment)
.Token(token)
.SDKConfig(config)
.SwitchUser();
using System;
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 static Com.Zoho.API.Authenticator.OAuthToken;
using SDKInitializer = Com.Zoho.Crm.API.Initializer;

namespace Com.Zoho.Crm.Sample.Threading.MultiUser
{
    /// <summary>
    /// 
    /// </summary>
    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;
        }

        public static void RunMultiThreadWithMultiUser()
        {
            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("abc@zoho.com");

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

            Token token1 = new OAuthToken.Builder()
                .ClientId("clientId")
                .ClientSecret("clientSecret")
                .RefreshToken("refreshToken")
                .RedirectURL("redirectURL")
                .Build();

            string resourcePath = "/Users/user_name/Documents/";

            DataCenter.Environment environment2 = USDataCenter.PRODUCTION;

            UserSignature user2 = new UserSignature("abc1@zoho.com");

            Token token2 = new OAuthToken.Builder()
                .ClientId("clientId")
                .ClientSecret("clientSecret")
                .RefreshToken("refreshToken")
                .Build();

            SDKConfig config = new SDKConfig.Builder().AutoRefreshFields(true).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 Cr's 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 static Com.Zoho.API.Authenticator.OAuthToken;
using SDKInitializer = Com.Zoho.Crm.API.Initializer;

namespace Com.Zoho.Crm.Sample.Threading.MultiUser
{
    public class SingleThread
    {
        public static void RunSingleThreadWithMultiUser()
        {
            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 user1 = new UserSignature("abc@zoho.com");

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

             Token token1 = new OAuthToken.Builder()
                .ClientId("clientId")
                .ClientSecret("clientSecret")
                .RefreshToken("refreshToken")
                .RedirectURL("redirectURL")
                .Build();

            string resourcePath = "/Users/user_name/Documents/";

            DataCenter.Environment environment = USDataCenter.PRODUCTION;

            UserSignature user2 = new UserSignature("abc1@zoho.com");
            
            Token token2 = new OAuthToken.Builder()
                .ClientId("clientId")
                .ClientSecret("clientSecret")
                .RefreshToken("refreshToken")
                .Build();

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

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

            new SingleThread().GetRecords("Leads");

            new SDKInitializer.Builder()
            .User(user2)
            .Environment(environment)
            .Token(token2)
            .SDKConfig(config)
            .SwitchUser();

            new SingleThread().GetRecords("Quotes");

        }

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

                RecordOperations recordOperation = new RecordOperations();

                APIResponse<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
                        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() 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 requires the following
            * 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 that requires the following
            * clientId -> OAuth client id.
            * clientSecret -> OAuth client secret.
            * refreshToken -> REFRESH token.
            * grantToken -> GRANT token.
            * id -> User unique id.
            * redirectURL -> OAuth redirect URL.
            */
            // if ID (obtained from persistence) is available
            Token token = new OAuthToken.Builder()
            .ClientId("clientId")
            .ClientSecret("clientSecret")
            .RefreshToken("refreshToken")
            .RedirectURL("redirectURL")
            .Build();

            /*
            * Create an instance of DBStore.
            * Host -> DataBase host name. Default "localhost"
            * DatabaseName -> DataBase name. Default "zohooauth"
            * UserName -> DataBase user name. Default "root"
            * Password -> DataBase password. Default ""
            * PortNumber -> DataBase port number. Default "3306"
            * TableName -> Table Name. Default value "oauthtoken"
            */
            //TokenStore tokenstore = new DBStore.Builder().Build();

            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(false).PickListValidation(false).Build();

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

            /**
            * Create an instance of RequestProxy class that takes the following parameters
            * Host -> Host
            * Port -> Port Number
            * User -> User Name
            * Password -> Password
            * UserDomain -> User Domain
            */
            RequestProxy requestProxy = new RequestProxy.Builder()
            .Host("proxyHost")
            .Port(proxyPort)
            .User("proxyUser")
            .Password("password")
            .UserDomain("userDomain")
            .Build();

            /*
            * The initialize method of Initializer class that takes the following arguments
            * User -> UserSignature instance
            * Environment -> Environment instance
            * Token -> Token instance
            * Store -> TokenStore instance
            * SDKConfig -> SDKConfig instance
            * ResourcePath -> resourcePath -A String
            * Logger -> Logger instance
            * RequestProxy -> RequestProxy instance
            */

			// Set the following in InitializeBuilder
			new SDKInitializer.Builder()
			.User(user)
			.Environment(environment)
			.Token(token)
			.Store(tokenstore)
			.SDKConfig(config)
			.ResourcePath(resourcePath)
			.Logger(logger)
			//.RequestProxy(requestProxy)
			.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));
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}