Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/310.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# protobuf网络流在返回数据之前被释放_C#_Asp.net Web Api_Ado.net_Protobuf Net_Sqlclient - Fatal编程技术网

C# protobuf网络流在返回数据之前被释放

C# protobuf网络流在返回数据之前被释放,c#,asp.net-web-api,ado.net,protobuf-net,sqlclient,C#,Asp.net Web Api,Ado.net,Protobuf Net,Sqlclient,我有一个webapi,它在从数据库列转换后返回一些protobuf net序列化项。项目的数量可能相当大,因此我希望避免在内存中具体化它们,而是将它们流出来。然而,在流抛出一个已处理的异常之前,我从未得到一个项目。如果我将列表具体化,它确实有效,但我希望避免它。 以下是我正在做的: private override async Task<IEnumerable<MyObj>> GetRawData(int id) { using(var sqlConn = Get

我有一个webapi,它在从数据库列转换后返回一些protobuf net序列化项。项目的数量可能相当大,因此我希望避免在内存中具体化它们,而是将它们流出来。然而,在流抛出一个已处理的异常之前,我从未得到一个项目。如果我将列表具体化,它确实有效,但我希望避免它。 以下是我正在做的:

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();
}
private override异步任务GetRawData(int-id)
{
使用(var sqlConn=GetOpenConnection())
{
使用(var sqlReader=(wait sqlConn.ExecuteReaderAsync(
_workingQuery,新的{id=id},60)
.ConfigureAwait(false)为SqlDataReader)
{
等待sqlReader.ReadAsync().ConfigureAwait(false);
使用(var stream=sqlReader.GetStream(0))
{
return _serializerModel.DeserializeItems(流,PrefixStyle.Base128,1)
}                
}
}
}
专用异步任务TransformData(int id)
{   
var data=await GetRawData().ConfigureAwait(false);
返回Ok(新建{Data=Data.Where(m=>…)。选择(m=>…)})
}
[HttpGet,Route(“api/myroute/{id}”]
公共异步任务GetObjs(int-id)
{
return wait TransformData();
}
但是,我最终在读取已处理流时出错。如何避免此错误?

这一行

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

它的长与短之处在于,您正在返回一个非枚举序列,并在它到达调用方之前关闭(处理)它所需的所有内容。您需要:

  • 急切地枚举序列(内存中的缓冲区)-例如,添加
    .ToList()
  • 重新构造代码,以便在迭代结束之前不会处理任何内容
对于后者,我倾向于使用迭代器块作为中心部分(在所有
wait
之后)

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;
         }
    }
}
bool dispose=true;
SqlConnection-conn=null;
//……其他
试一试{
康涅狄格州=。。。
…等待。。。
var结果=内部方法(连接、流等)
//^^^目前已知/从DB获得的所有信息
dispose=false;//将生存期管理交给内部方法
返回结果;
}最后{
如果(处置){
使用(conn){}
//.//
}
}
IEnumerable内部(…){//不异步
使用(康涅狄格州)
使用(流){
foreach(var项在…){
收益回报项目;
}
}
}

显示serializerModel类。它是protobuf net typemodel对象。我不这么认为,因为Where和Select是不具体化枚举的惰性运算符。这些对象将具体化,但一旦写入json流,它们就可能被丢弃。如果我将它们写入列表,我将立即支付具体化的成本t then and there,然后我只能在对象都写入流之后扔掉它们。经过进一步的调查,我同意你的第一个陈述。但是第二个陈述不是真的。new{Data=…}具体化整个Json对象,然后发送它。为了流化结果,您需要让Web API返回一个流。问题仍然是,在流被读取时,最终不处理流、读取器或连接对象。我可以在某种程度上解决这个问题,但我不希望控制器处理连接。as
GetRawData
应该拥有它,仅仅处理流不会处理连接。推送内容流是一个有趣的想法。使用我在回答的后半部分中建议的方法,您可以有一个单独的函数读取ReaderStream并将WriterStream返回到响应。控制器将till负责关闭WriterStream,关闭WriterStream也会关闭ReaderStream,但没什么可做的。谢谢marc。我最终采用了一种非常类似的方法。我很好奇,为什么返回一个调用yield return iterator块的函数可以让这一切在返回同样懒惰的iterat块的情况下发挥作用或者反序列化项的导致它立即被处置。是因为我将资源的处置与迭代的结束捆绑在一起吗?
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;
         }
    }
}