Find the full sample app demonstrating this problem here.
I'm trying to use OWIN middleware to populate a static identifier that will be accessible by a WebForms page, but I'm noticing that with certain types this does not behave intuitively - specifically AsyncLocal<T>
.
Consider the following working sample, using an int
:
Simple static container
public static class Container
{
public static int CallId { get; set; }
}
Simple OWIN configuration
[assembly: OwinStartup(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use((context, next) =>
{
Container.CallId = 5;
return next.Invoke();
});
}
}
Simple Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
if (Container.CallId != 5)
{
throw new Exception("What happened to CallId");
}
}
This functions as expected. The middleware sets CallId
to 5, the WebForms page sees a 5. The exception is not thrown.
Here's where things break intuition, if I want to use an AsyncLocal<int>
for my CallId, the value is no longer available in the WebForms page. E.g.
Broken Container
public static class Container
{
public static AsyncLocal<int> CallId { get; set; }
}
Broken OWIN configuration
[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();
});
}
}
Broken Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
if (Container.CallId.Value != 5)
{
throw new Exception("What happened to CallId");
}
}
This does not function as expected. An identifier is created in the middleware, but is not available in the page. The exception is thrown.
Why?
I'm struggling to piece together why this update breaks intuition; I can't think of anything that could be causing the value to disappear.
On_Load
. Container
is static, so only one instance exists in the domain. MyType
, CallId
doesn't return to null
in the WebFormWhere did the static CallId
value go?
If you want to see this for yourself, here is a demo github repo.
You are using it wrong. Use the local to hold the desired value.
public static class Container {
private static AsyncLocal<int> current = new AsyncLocal<int>();
public static int CallId {
get {
return current.Value;
}
set {
current.Value = value;
}
}
}
And OWIN configuration remains as it did before.
[assembly: OwinStartup(typeof(Startup))]
public class Startup {
public void Configuration(IAppBuilder app) {
app.Use((context, next) => {
Container.CallId = 5;
return next.Invoke();
});
}
}
the AsyncLocal<T>
class will now persist the value across asynchronous flows across threads.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With