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 异步/等待的主要内容,如果未能解决你的问题,请参考以下文章