我对异步调用不是很熟悉,但是我遇到了一些我认为是一个奇怪的问题并且不确定它为什么会发生。
CONCEPT
调用函数来检索数据。如果数据存储在我们的缓存中,我们使用Redis
作为我们的缓存(如果我在其他线程上读取数据应该如何存储),然后返回它。否则,调用第三方库(特别是.Net的Force.com Toolkit,但我怀疑它很重要),它使用async
调用并缓存结果。
我创建了一个有效的synchronous
方法,但现在我想将其更改为asynchronous
。
同步
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;
}
异步完整代码
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);
}
}
网页
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 />");
}
}
同步版本效果很好,但当我将其转换为异步时,我在行HttpContext.Current.Cache.Insert(...)
异常详细信息: System.NullReferenceException:未将对象引用设置为对象的实例。
目前,此网站是WebForms
网站,因此要调试我使用Response.Write()
。当我尝试显示cacheName
变量时,我得到相同的结果。
异常详细信息: System.NullReferenceException:未将对象引用设置为对象的实例。
Line 30: HttpContext.Current.Response.Write(cacheName + "<br>");
当我进行cacheName
通话时,变量async
是否会丢失?我非常怀疑,但我不确定如何继续。
希望有人可以提供帮助。
答案 0 :(得分:2)
.NET中与Web相关的框架通常使用线程静态变量,如HttpContext.Current
,OperationContext.Current
,WebOperationContext.Current
等。由于异步方法调用后执行可能会在不同的线程中继续,线程静态变量丢失。
这是一个控制台应用程序,只是为了表明我的意思:
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Test
{
static void Main()
{
MyAsyncMethod().Wait();
Console.ReadLine();
}
[ThreadStatic]
static int MyContext = 666;
static async Task MyAsyncMethod()
{
MyContext = 555;
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " " + MyContext);
using (var client = new HttpClient())
{
var html = await client.GetStringAsync("http://google.com");
}
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " " + MyContext);
}
}
}
因此,您需要在调用异步方法之前保存它们:
public static async Task<Models.Description.ObjectDescription> Describe(ForceClient forceClient, string sObject)
{
var ctx = HttpContext.Current; //<-- *****
Models.Description.ObjectDescription result;
var cacheName = "Salesforce_Object_" + sObject;
if (ctx.Cache[cacheName] != null)
{
result = (Models.Description.ObjectDescription) ctx.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
ctx.Cache.Insert(
cacheName,
result,
null,
DateTime.UtcNow.AddSeconds(expiration),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null);
}
}
return result;
}
答案 1 :(得分:0)
我需要在页面中添加以下内容才能使其正常工作:
<%@ Page Async="true" %>
并从
更改Page_Load()
方法的签名
protected void Page_Load(object sender, EventArgs e) {...}
到
protected async void Page_Load(object sender, EventArgs e) {...}