为什么AsyncLocal从OWIN中间件不流到WebForms页面?

时间:2018-08-10 03:38:28

标签: c# asp.net webforms owin katana

找到演示此问题的完整示例应用here

我正在尝试使用OWIN中间件来填充可通过WebForms页面访问的静态标识符,但是我注意到,对于某些类型,它不能直观地表现-特别是AsyncLocal<T>

使用int,考虑以下工作示例:

简单的静态容器

public static class Container
{
    public static int CallId { get; set; }
}

简单的OWIN配置

[assembly: OwinStartup(typeof(Startup))]

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Use((context, next) =>
        {
            Container.CallId = 5;

            return next.Invoke();
        });
    }
}

简单的Default.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
    if (Container.CallId != 5)
    {
        throw new Exception("What happened to CallId");
    }
}

此功能按预期运行。中间件将CallId设置为5,WebForms页面看到5。不会引发异常。


这是打破直觉的地方,如果我想对我的CallId使用AsyncLocal<int>,则该值在WebForms页面中不再可用。例如

损坏的容器

public static class Container
{
    public static AsyncLocal<int> CallId { get; set; }
}

OWIN配置损坏

[assembly: OwinStartup(typeof(Startup))]

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Use((context, next) =>
        {
            AsyncLocal<int> asyncId = new AsyncLocal<int>();
            asyncId.Value = 5
            Container.CallId = asyncId;

            return next.Invoke();
        });
    }
}

损坏的Default.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
    if (Container.CallId.Value != 5)
    {
        throw new Exception("What happened to CallId");
    }
}

这不会不能正常运行。标识符在中间件中创建,但在页面中不可用。 引发异常

为什么?

我正在努力拼凑为什么此更新会破坏直觉;我想不出任何可能导致价值消失的东西。

  • 在执行的中间件组件和Webform页面的On_Load之间,进程,域和线程ID均保持不变。
  • Container是静态的,因此域中仅存在一个实例。
  • 行为上的差异并不取决于原始类型和引用类型;我用简单的MyType尝试过同样的事情,CallId在WebForm中不会返回null

静态CallId值去哪里了?

如果您想亲自看看,这里是demo github repo

1 个答案:

答案 0 :(得分:0)

您使用错误。使用本地变量保存所需的值。

public static class Container {
    private static AsyncLocal<int> current = new AsyncLocal<int>();

    public static int CallId { 
        get {
            return current.Value;
        } 
        set {
            current.Value = value;
        }
    }
}

OWIN配置保持不变。

[assembly: OwinStartup(typeof(Startup))]
public class Startup {
    public void Configuration(IAppBuilder app) {
        app.Use((context, next) => {
            Container.CallId = 5;
            return next.Invoke();
        });
    }
}

AsyncLocal<T>类现在将在跨线程的异步流中保留该值。