This project has moved and is read-only. For the latest updates, please go here.

Object gets disposed too early?

Mar 2, 2014 at 12:04 AM
Edited Mar 2, 2014 at 12:22 AM
This puzzles me. I have this OWIN/Katana middleware:

 class M1 : OwinMiddleware
    {
        public M1(OwinMiddleware next) : base(next) { }
        public override Task Invoke(IOwinContext context)
        {
            return Task.Run(() =>
            {
                Thread.Sleep(200); ; // do something
                string reqUrl = context.Request.Uri.ToString(); //<- throws exception
            })
                .ContinueWith(t => this.Next.Invoke(context));

        }
    }
and then a Startup class:
   public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.Use((context, next) =>
            {
                return Task.Run(() =>
                {
                }).ContinueWith(t => next());
            });

            app.Use<M1>();
        }
    }
Running this throws an ObjectDisposedException in M1:
Cannot access a disposed object. Object name: 'System.Net.HttpListenerRequest'.
Stack trace:
at System.Net.HttpListenerRequest.CheckDisposed() at System.Net.HttpListenerRequest.GetKnownHeader(HttpRequestHeader header) at System.Net.HttpListenerRequest.get_UserHostName() at Microsoft.Owin.Host.HttpListener.RequestProcessing.RequestHeadersDictionary.TryGetValue(String key, String[]& value) at Microsoft.Owin.HeaderDictionary.TryGetValue(String key, String[]& value) at Microsoft.Owin.Infrastructure.OwinHelpers.GetHeaderUnmodified(IDictionary2
headers, String key) at
Microsoft.Owin.Infrastructure.OwinHelpers.GetHeader(IDictionary2 headers, String key) at Microsoft.Owin.Infrastructure.OwinHelpers.GetHost(IOwinRequest request) at Microsoft.Owin.OwinRequest.get_Host()
If I remove that anonymous middleware before app.Use<M1>(); then no exception is thrown.

Am I doing it wrong ?

Edit: Just tried with anonymous handlers (app.use((context, next)=>{})), and second handler will fail. So looks like request gets disposed in a first handler - is that right ?
Mar 2, 2014 at 12:25 PM
TL;DR: your issue is due to a race condition caused by an incorrect use of ContinueWith.

As you can see, OwinMiddleware.Invoke method returns a Task object. So, each time you call this.Next.Invoke(context) or next() in ContinueWith, it actually completes immediately and returns a Task what will be exposed in the Task.Result property.
This is clear when you use a delegate-style lambda :
.ContinueWith(t => {
    return this.Next.Invoke(context);
});
But... the Task returned by the lambda function is not the one returned by ContinueWith which in fact returns a Task<Task> object (which is here down-cast to a Task). Thus, OWIN won't await for the child task to complete and will dispose the request just after the immediate Task is returned, i.e when the lambda is executed.

You have two options to solve this: either dive into the marvelous world of async/await (by far your best option) or use the Unwrap extension method included in mscorlib just after your ContinueWith calls: it will build a new Task object for you that will internally await for both the parent and the child tasks:
class M1 : OwinMiddleware
    {
        public M1(OwinMiddleware next) : base(next) { }
        public override Task Invoke(IOwinContext context)
        {
            return Task.Run(() =>
            {
                Thread.Sleep(200); ; // do something
                string reqUrl = context.Request.Uri.ToString(); //<- throws exception
            })
                .ContinueWith(t => this.Next.Invoke(context)).Unwrap();

        }
    }

   public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.Use((context, next) =>
            {
                return Task.Run(() =>
                {
                }).ContinueWith(t => next()).Unwrap();
            });

            app.Use<M1>();
        }
    }