如何从通过Task.Factory.StartNew创建的线程访问应用程序上下文

时间:2014-01-24 15:21:26

标签: c# wcf async-await c#-5.0 applicationcontext

我有一个在主应用程序线程中运行的方法,但是为长时间运行的操作创建了新的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
        }
    }
}

只是为了澄清,这里是调用堆栈(我无法在必需的方法中传递我的上下文): Call stack

所以:

  1. 我无法将上下文传递给Channel的方法,因为它没有任何意义;
  2. 我无法从Channel中的上下文中保存所需的参数,因为它是代理类;
  3. 我无法在CalculatorMessageInspector中保存所需的参数,因为它是在当前上下文已经为空的位置创建的。
  4. 任何人都可以建议任何方法如何在另一个线程中保持相同的上下文?或者,至少,如何从方法层次结构中标记为“2”的位置传递参数?也许,我可以用SynchronizationContext以某种方式来实现它?非常感谢任何建议。

    更新

    AppContext.Current视为与HttpContext.Current != null ? HttpContext.Current.Items[AppContextKey] : null

    相同

    更新2 所以看来,在这种情况下,没有共同的解决方案来坚持上下文。因此,在这种具体情况下(这是非常具体的)唯一适用的解决方案是使用闭包捕获所需的参数,然后保存在一个对象中,这将在必需的方法中可用(对于我来说,为{{1}的实现者添加属性但是,这个解决方案有点奇怪)。因此,最适用的解决方案是通过同步调用丢弃异步包装,因此IEndpointBehavior永远不会“消失”。那时马克斯蒂芬的答案就是正确的。

1 个答案:

答案 0 :(得分:2)

要在ASP.NET上使用asyncawait,必须以.NET 4.5为目标并关闭quirks模式。执行此操作的最佳方法是在web.config中设置/configuration/system.web/httpRuntime@targetFramework="4.5"

此外,您不应在ASP.NET上使用Task.RunTask.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;
        }
    }
}