I'm not very familiar with asynchronized calls but I ran into a bit of what I think is a weird issue and not sure why it's happening.
CONCEPT
Call a function to retrieve data. If data is stored in our cache, we use Redis
as our cache (in case it matters on how the data should be stored as I've read on other threads), then return it. Otherwise, make a call to a third party library (specifically Force.com Toolkit for .Net, but I doubt it matters) which uses async
calls and cache the results.
I created a synchronous
method which works, but now I want to change it to be asynchronous
.
Synchronous
public static Models.Description.ObjectDescription Describe(ForceClient forceClient, string sObject)
{
Models.Description.ObjectDescription result;
var cacheName = "Salesforce_Object_" + sObject;
if (HttpContext.Current.Cache[cacheName] != null)
{
result = (Models.Description.ObjectDescription) HttpContext.Current.Cache[cacheName];
}
else
{
result = forceClient.DescribeAsync<Models.Description.ObjectDescription>(sObject).Result;
if (result != null)
{
var expiration = 30; // testing, this will be read from a global variable
HttpContext.Current.Cache.Insert(
cacheName,
result,
null,
DateTime.UtcNow.AddSeconds(expiration),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null);
}
}
return result;
}
Asynchronous full code
public static class Description {
public static async Task<Models.Description.ObjectDescription> Describe(ForceClient forceClient, string sObject)
{
Models.Description.ObjectDescription result;
var cacheName = "Salesforce_Object_" + sObject;
if (HttpContext.Current.Cache[cacheName] != null)
{
result = (Models.Description.ObjectDescription) HttpContext.Current.Cache[cacheName];
}
else
{
/*
* only line that changed from above
*/
result = await forceClient.DescribeAsync<Models.Description.ObjectDescription>(sObject);
if (result != null)
{
var expiration = 30; // testing, this will be read from a global variable
HttpContext.Current.Cache.Insert(
cacheName,
result,
null,
DateTime.UtcNow.AddSeconds(expiration),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null);
}
}
return result;
}
public static async Task<IList<Models.Description.PicklistValues>> Picklist(ForceClient forceClient, string sObject, string fieldName) {
var results = await Describe(forceClient, sObject);
var field = results.Fields.SingleOrDefault(f => f.Name.Equals(fieldName));
return field != null ? field.PickListValues : new List<Models.Description.PicklistValues>();
}
}
public static class Lead {
public static async Task<IList<Models.Description.PicklistValues>> Picklist(ForceClient forceClient, string fieldName) {
return await Description.Picklist(forceClient, "Lead", fieldName);
}
}
Page
protected void Page_Load(object sender, EventArgs e) {
/* when I try to `await` this i get an error stating:
* The 'await' operator can only be used with an async method.
* ...but when I change Page_Load to
* protected async Task Page_Load(object sender, EventArgs e) {...}
* ... and 'await' the call, My results are blank
*/
var result = Lead.Picklist(new Authentication.ForceClient(), "Source__c").Result;
foreach (var value in result) {
Response.Write("- " + value.Value + "<br />");
}
}
The Synchronous version works great, but when I convert it to Asynchronous I get an error on the line HttpContext.Current.Cache.Insert(...)
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Currently, this site is a WebForms
website so to debug I use Response.Write()
. When I try to display the cacheName
variable, I get the same results.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Line 30: HttpContext.Current.Response.Write(cacheName + "<br>");
Is the variable cacheName
being lost when I make the async
call? I highly doubt it, but I'm not sure how to proceed.
Hopefully someone can help.