我对异步调用不太熟悉,但我遇到了一些我认为很奇怪的问题,并且不确定为什么会发生。
概念
调用函数来检索数据。如果数据存储在我们的缓存中,我们使用 Redis
作为我们的缓存(如果数据应该如何存储,就像我在其他线程上读取的那样),然后返回它。否则,调用使用 async
的第三方库(特别是 Force.com Toolkit for .Net,但我怀疑它是否重要)调用并缓存结果。
我创建了一个 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(...)
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
目前,该网站是 WebForms
网站以便调试我使用 Response.Write()
。当我尝试显示cacheName
时变量,我得到相同的结果。
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Line 30: HttpContext.Current.Response.Write(cacheName + "<br>");
变量是cacheName
当我制作 async
时迷路了称呼?我非常怀疑,但我不知道如何继续。
希望有人能帮忙。
最佳答案
.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;
}
关于c# - 异步调用后丢失变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45717656/