c# - 从任务继续写入 ASP.NET 响应输出流

标签 c# asp.net asynchronous .net-4.0 task-parallel-library

我有一个 http 处理程序,它应该写入以输出一些文本。文本内容是异步检索的,所以我想像这样在 ProcessRequest 方法中写入响应流:

GetContent().ContinueWith(task => 
{
    using (var stream = task.Result)
    {
        stream.WriteTo(context.Response.OutputStream);
    }
});

但是,我得到一个带有堆栈跟踪的 NullReferenceException

in System.Web.HttpWriter.BufferData(Byte[] data, Int32 offset, Int32 size, Boolean needToCopyData)
   in System.Web.HttpWriter.WriteFromStream(Byte[] data, Int32 offset, Int32 size)
   in System.Web.HttpResponseStream.Write(Byte[] buffer, Int32 offset, Int32 count)
   in System.IO.MemoryStream.WriteTo(Stream stream)
   in SomeHandler.<>c__DisplayClass1.<ProcessRequest>b__0(Task`1 task) in SomeHandler.cs:line 33
   in System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
   in System.Threading.Tasks.Task.Execute()

如果我不使用 ContinueWith 并在 task.Wait() 之后写入响应 - 没有错误,但显然这不是解决方案。

var task = GetContent();
task.Wait();
using (var stream = task.Result)
{
    stream.WriteTo(context.Response.OutputStream);
}

如何消除这个错误? (使用.net 4.0)

最佳答案

您需要实现IHttpAsyncHandler。检查"Walkthrough: Creating an Asynchronous HTTP Handler" .

最重要的是,您可以使用 async/await 复制流(注意下面的 CopyAsync)。为了能够在 VS2012+ 中使用 async/await 和目标 .NET 4.0,添加 Microsoft.Bcl.Async打包到您的项目中。

这样,就不会不必要地阻塞任何线程。一个完整的例子(未经测试):

public partial class AsyncHandler : IHttpAsyncHandler
{
    async Task CopyAsync(HttpContext context)
    {
        using (var stream = await GetContentAsync(context))
        {
            await stream.CopyToAsync(context.Response.OutputStream);
        }
    }

    #region IHttpAsyncHandler
    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        return new AsyncResult(cb, extraData, CopyAsync(context));
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        // at this point, the task has compeleted
        // we use Wait() only to re-throw any errors
        ((AsyncResult)result).Task.Wait();
    }

    public bool IsReusable
    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        throw new NotImplementedException();
    }
    #endregion

    #region AsyncResult
    class AsyncResult : IAsyncResult
    {
        object _state;
        Task _task;
        bool _completedSynchronously;

        public AsyncResult(AsyncCallback callback, object state, Task task)
        {
            _state = state;
            _task = task;
            _completedSynchronously = _task.IsCompleted;
            _task.ContinueWith(t => callback(this), TaskContinuationOptions.ExecuteSynchronously);
        }

        public Task Task
        {
            get { return _task; }
        }

        #region IAsyncResult
        public object AsyncState
        {
            get { return _state; }
        }

        public System.Threading.WaitHandle AsyncWaitHandle
        {
            get { return ((IAsyncResult)_task).AsyncWaitHandle; }
        }

        public bool CompletedSynchronously
        {
            get { return _completedSynchronously; }
        }

        public bool IsCompleted
        {
            get { return _task.IsCompleted; }
        }
        #endregion
    }
    #endregion
}

关于c# - 从任务继续写入 ASP.NET 响应输出流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24510448/

相关文章:

asp.net - 我怎样才能在 10 天后约会?在 asp.net 中

asp.net - IOS 上无服务器多人游戏

javascript - 异步ajax请求锁定浏览器

c# - 私有(private)方法?

c# - 使用 DropDownListFor 添加 'required' 属性

c# - 如何向 SyndicateFeed 对象的序列化输出添加换行符?

c# - Action<T> 与虚拟方法

c# - 使用 C#,如何通过 POP3 从 Gmail 服务器检索电子邮件列表

mongodb - JS Node 异步处理事件

javascript - 如何在组件的复选框更改时异步加载数据并将其附加到模型?