我有一个在主应用程序线程中运行的方法,但是为长时间运行的操作创建了新的Task
,并且await
是它的结果。
public async Task<CalculatorOutputModel> CalculateXml(CalculatorInputModel input)
{
// 1
return await Task.Factory.StartNew(
new Func<CalculatorOutputModel> (() =>
{
// 2
using (var ms = Serializer.Serialize(input))
{
ms.Position = 0;
using (var rawResult = Channel.RawGetFrbXmlOutputs(ms)) // 3
{
var result = Parser.Parse(rawResult);
return result;
}
}
}),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
我的问题是点(1)和(2)中的AppContext“有点”不同。在第1点中,Current
属性中有通常的应用程序上下文,它包含我需要的所有数据。在第2点中,如您所知,null
中有AppContext.Current
引用,因此我无法访问任何数据。在第2点访问上下文的问题似乎很容易,只是“捕获”局部变量中的当前上下文或将其作为参数传递。对我来说问题更加困难,因为我需要访问标记为“3”的深处某处的上下文
类本身派生自System.ServiceModel.ClientBase<TChannel>
,我需要访问上下文的地方是实现IClientMessageInspector
的类。
class CalculatorMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
if (AppContext.Current != null)
{
// never goes here
}
}
}
只是为了澄清,这里是调用堆栈(我无法在必需的方法中传递我的上下文):
所以:
Channel
的方法,因为它没有任何意义; Channel
中的上下文中保存所需的参数,因为它是代理类; CalculatorMessageInspector
中保存所需的参数,因为它是在当前上下文已经为空的位置创建的。 任何人都可以建议任何方法如何在另一个线程中保持相同的上下文?或者,至少,如何从方法层次结构中标记为“2”的位置传递参数?也许,我可以用SynchronizationContext
以某种方式来实现它?非常感谢任何建议。
更新
AppContext.Current
视为与HttpContext.Current != null ? HttpContext.Current.Items[AppContextKey] : null
更新2
所以看来,在这种情况下,没有共同的解决方案来坚持上下文。因此,在这种具体情况下(这是非常具体的)唯一适用的解决方案是使用闭包捕获所需的参数,然后保存在一个对象中,这将在必需的方法中可用(对于我来说,为{{1}的实现者添加属性但是,这个解决方案有点奇怪)。因此,最适用的解决方案是通过同步调用丢弃异步包装,因此IEndpointBehavior
永远不会“消失”。那时马克斯蒂芬的答案就是正确的。
答案 0 :(得分:2)
要在ASP.NET上使用async
和await
,必须以.NET 4.5为目标并关闭quirks模式。执行此操作的最佳方法是在web.config中设置/configuration/system.web/httpRuntime@targetFramework="4.5"
。
此外,您不应在ASP.NET上使用Task.Run
或Task.Factory.StartNew
。所以你的代码应该更像这样:
public CalculatorOutputModel CalculateXml(CalculatorInputModel input)
{
using (var ms = Serializer.Serialize(input))
{
ms.Position = 0;
using (var rawResult = Channel.RawGetFrbXmlOutputs(ms))
{
var result = Parser.Parse(rawResult);
return result;
}
}
}