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