WebApi
WebApi copied to clipboard
When I use the $batch endpoint I get "cannot access a closed stream"
I am using Microsft.AspNetCore.Odata version 7.2.2
If I configure the following services:
services.AddOData();
mvcBuilder.AddMvcOptions(options => options.EnableEndpointRouting = false);
And then I have
app.UseODataBatching();
app.UseMvc(routeBuilder =>
{
routeBuilder.MapODataServiceRoute("ODataRoute", "odata", builder => builder
.AddDefaultODataServices()
.AddService<IEdmModel>(ServiceLifetime.Singleton, s => model)
.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp => ODataRoutingConventions.CreateDefaultWithAttributeRouting("ODataRoute", routeBuilder))
.AddService<ODataUriResolver>(ServiceLifetime.Singleton, s => new AlternateKeysODataUriResolver(model))
.AddService<ODataBatchHandler>(ServiceLifetime.Singleton, s => new DefaultODataBatchHandler()));
});
The problem is that if I have a batch where one of the calls in the batch returns data, then when I submit a batch I get an error like this:
System.ObjectDisposedException: Cannot access a closed Stream.
 at System.IO.MemoryStream.get_Length()
 at Microsoft.AspNet.OData.Batch.ODataBatchResponseItem.WriteMessageAsync(ODataBatchWriter writer, HttpContext context)
 at Microsoft.AspNet.OData.Batch.ODataBatchContent.WriteToResponseMessageAsync(IODataResponseMessage responseMessage)
 at Microsoft.AspNet.OData.Batch.DefaultODataBatchHandler.ProcessBatchAsync(HttpContext context, RequestDelegate nextHandler)
 at Microsoft.AspNet.OData.Batch.ODataBatchMiddleware.Invoke(HttpContext context)

If I make the controller methods not return a payload then they work fine.
I have tried both a JSON formatted batch request and a multipart MIME batch request.
How do I avoid these stream errors?
@rjarrattmsft What version of framework (ASP.NET Core 2x or 3x) are you targeting?
@rjarrattmsft What version of framework (ASP.NET Core 2x or 3x) are you targeting?
I am targeting .NET Core 2.2.
I have found that adding a Prefer header with a value of "return=minimal" resolves the problem. However there is no documentation to say that you should use this header and if you want to get the return data from the sub requests you have to query again outside a batch. I made sure the pipepine only had Odata and MVC, I removed all our other pipeline components and the problem still happens. It feels like there is a bug somewhere.
I have simplified this to a repro that just uses vanilla code, the repro contains two unit tests which show the failure. It looks to me like the stream for the response for each request gets closed and disposed before the batch handler gets the response in order to aggregate it into an overall response. The repro is attached. ODataBatchRepro.zip
I was testing the $batch feature out in our project as well (.NET Core 2.2, OData 7.2.1) with just a single GET request and received the same error as well. I can see the requested GET endpoint being called and the SQL query that it uses in my console but it does appear that the batch handler is attempting to write to the response after it's been closed and disposed.
Thanks for the reports and easy repo. We have a fix that should go out in the next release. I'll update when it's available to try out.
Was your issue resolved @rjarrattmsft ? You can try the latest version
This was more than 2.5 years ago and I no longer remember, although I think it was fixed. I have moved on to a different project since and it is not easy for me to check.
Closing this issue due to inactivity. If this issue still persists, feel free to create a new issue.