WCF REST HttpContext.Current 异步/等待

Posted

技术标签:

【中文标题】WCF REST HttpContext.Current 异步/等待【英文标题】:WCF REST HttpContext.Current Async/await 【发布时间】:2018-07-27 20:09:33 【问题描述】:

我有一个使用 WCF REST 在 IIS 中运行的 ASP.NET 应用程序。

我需要为当前的 HTTP 请求存储变量,为此我使用 HttpContext.Current.Items。我存储了一些我在global.asax 中设置的请求 ID,以便我可以在我的服务中更深入地使用它。我的服务正在执行一些 I/O 操作,因此我最近将它们从同步更改为异步。问题是在第一个await 之后,HttpContext.Current 变为空,因此我无法访问存储在HttpContext.Current.Items 中的变量。

我的 global.asax:

    protected void Application_BeginRequest(object sender, EventArgs e)
    
        HttpContext.Current.Items["CurrentRequestId"] = SetRequestId();
    

我的 WCF 合同:

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WcfService

    [OperationContract]
    [WebGet(UriTemplate = "Operation")]
    public async Task<bool> Operation()
    
        var context = HttpContext.Current; // Current context available here

        await Task.Delay(1000).ConfigureAwait(true); // tried with both ConfigureAwait(true) and ConfigureAwait(false)

        context = HttpContext.Current; // Current context is always null here

        return true;
    

我试图在我的 web.config 文件中添加这些键,但它没有改变任何东西。

<appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
    <add key="wcf:disableOperationContextAsyncFlow" value="false"/>
</appSettings>

我正在使用 .net 4.6.2

<httpRuntime targetFramework="4.6.2" />

在使用 ASP.NET 在 WCF REST 中等待异步方法后,是否可以保留 HttpContext.Current?我可以用HttpContext.Current.Items 的替代方法来实现我想要做的事情吗?

编辑: 这是一个简化的示例,但 HttpContext.Current 使用得更深,我不希望在等待之前收集它并将其一直传递给每个方法.

【问题讨论】:

在等待之后使用你的context 你有没有找到解决这个问题的方法? @Bouke 我迁移到了 ASP.NET Core,所以不再有这个问题,但如果我没记错的话,我必须使用 AsyncLocal 而不是依赖于HttpContext.Current. 【参考方案1】:

这个问题其实和异步代码无关;它与从(非请求)线程池线程中检索上下文变量有关。

链接博客中推荐的技术(包装 HttpContext 并将其提供给工作线程)非常危险。 HttpContext 设计为一次只能从一个线程访问,AFAIK 根本不是线程安全的。所以在不同的线程之间分享它是在要求一个伤害的世界。

所以我强烈建议你在你的异步方法之前从 HttpContext 获取你需要的所有东西然后使用它。

【讨论】:

【参考方案2】:

我在 WCF 中遇到了类似的问题,在等待任务后我丢失了 OperationContext。实际上在等待之后 - 从线程池分配的线程没有上下文(基本上它是一个不同的线程)。我们创建了一个通用包装器方法来解决这个问题。下面我尝试在您的问题的上下文中进行相同的转换。试试看:

[OperationContract]
[WebGet(UriTemplate = "Operation")]
public async Task<bool> Operation()
    
        RunWithOperationContext(()=> 
        await Task.Delay(1000); 
        return true;
    
public static Task<TResult> RunWithOperationContext<TResult>(Func<TResult> function)
        
            var context = HttpContext.Current;
            var result = await function();
            HttpContext.Current = context;
            return result;
        

大卫关于线程安全的观点 - 我考虑过并做了一些尽职调查以确保这是线程安全的。

【讨论】:

我认为使用它没有任何意义。与异步调用后直接使用 context 有什么不同。 Operation() var context = HttpContext.Current; result = await something(); use context here 同意。但正如我提到的,我想把这个东西放在一个地方,而不是每次都重复。 我也看不出你是如何把这个东西放在一个地方。每次异步调用后都需要HttpContext.Current = context; 我的意思是我不想为每个async 任务重复var context = HttpContext.Current; SomeOperation(); HttpContext.Current = context; 我会用RunWithOperationContext包装我的异步操作

以上是关于WCF REST HttpContext.Current 异步/等待的主要内容,如果未能解决你的问题,请参考以下文章

WCF REST 方法和非 REST 方法

WCF + REST,增加 MaxStringContentLength

如何从基于 REST 的 WCF 服务中读取授权标头?

WCF 4.0 REST 用户名密码认证

用于 Xamarin 表单的 Rest + WCF 集成

WCF 服务的 REST / SOAP 端点