c# - 异步调用后丢失变量

标签 c# caching asynchronous

我对异步调用不太熟悉,但我遇到了一些我认为很奇怪的问题,并且不确定为什么会发生。

概念 调用函数来检索数据。如果数据存储在我们的缓存中,我们使用 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.CurrentOperationContext.CurrentWebOperationContext.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/

相关文章:

c# - LINQ to SQL : Invalid column name 'DepartureGate' , 即使该列存在

c# - 全局注册键盘按下

Spring @Cacheable 不缓存

wordpress - Redis 仅在数据库中使用 1 个键

javascript - 尝试读取 React js 中 API 返回的响应时出现未定义问题

javascript - 不阻塞的回调

c# - .NET Core (MYSQL) Generic Repository AutoWiring 使用 Autofac

c# - 如何在 Windows Phone 8 中释放图像缓存/内存?

caching - 我们可以在堆内存上使用非临时 mov 指令吗?

c# - 任务最小起订量等待