protobuf-net Stream在返回数据之前被释放

时间:2014-12-12 15:28:45

标签: c# asp.net-web-api ado.net protobuf-net sqlclient

我有一个webapi,它从数据库列转换后返回一些protobuf-net序列化项。项目的数量可能相当大,所以我想避免在内存中实现它们而不是将它们流出来,但是,在我的Stream抛出已经处理的异常之前,我从来没有得到过一个。如果我实现列表它确实有效,但我想避免它。 这就是我正在做的事情::

private override async Task<IEnumerable<MyObj>> GetRawData(int id)
{
    using(var sqlConn = GetOpenConnection())
    {
        using (var sqlReader =(await sqlConn.ExecuteReaderAsync(
            _workingQuery,new {id = id}, 60)
                    .ConfigureAwait(false) as SqlDataReader))
        {
            await sqlReader.ReadAsync().ConfigureAwait(false);

            using (var stream = sqlReader.GetStream(0))
            {
               return _serializerModel.DeserializeItems<MyObj>(stream, PrefixStyle.Base128, 1)
            }                
        }
    }
}

private async Task<IHttpActionResult> TransformData(int id)
{   
    var data=await GetRawData().ConfigureAwait(false);
    return Ok(new {Data=data.Where(m=>...).Select(m=>...)})
}
[HttpGet,Route("api/myroute/{id}"]
public async Task<IHttpActionResult> GetObjs(int id)
{
     return await TransformData();
}

然而,我最终收到有关阅读已处置流的错误。如何避免此错误?

2 个答案:

答案 0 :(得分:1)

这一行

return _serializerModel.DeserializeItems<MyObj>(stream, PrefixStyle.Base128, 1)

只返回索引器。您似乎意识到结果尚未实现。但是,在返回后,using块会处理您的Stream

解决方案是让GetRawData返回 Stream 。然后,在TransformDatausing内的Stream内,您反序列化,过滤并返回结果。

第二个潜在问题是您当前的方法会处理整个结果,然后立即将其发送给客户端。

要让 Web API 发送流式搜索结果,您需要使用HttpResponseMessage,并将Stream传递给它的Content媒体资源

以下是一个例子:http://weblogs.asp.net/andresv/asynchronous-streaming-in-asp-net-webapi

如果您想将Stream流式传输到另一个Stream(我知道,这听起来很有趣),您需要保持ReaderStream打开,直到WriterStream完成写作。这是一个高级代表:

using (ReaderStream)
{
    using (WriterStream)
    {
        // write object from ReaderStream to WriterStream
    }
}

答案 1 :(得分:1)

它的长短是你返回一个非枚举序列,并在它到达调用者之前关闭(处理)它所需的一切。您将需要:

  • 急切地枚举序列(内存中的缓冲区) - 例如,添加.ToList()
  • 重构代码,以便在迭代的 end 之前不会处理任何内容

对于后者,我很想在中心部分使用迭代器块(在所有await之后)。类似的东西:

bool dispose = true;
SqlConnection conn = null;
//...others
try {
   conn = ...
   ...await...
   var result = InnerMethod(conn, stream, ..., ) 
    // ^^^ everything now known / obtained from DB
    dispose = false; // handing lifetime management to the inner method
    return result;
} finally {
    if(dispose) {
        using(conn) {}
        //.//
    }
}

IEnumerable<...> Inner(...) { // not async
    using (conn)
    using (stream) {
         foreach(var item in ...) {
             yield return item;
         }
    }
}