When a batch request is received in Web API, this request is spawn into multiple requests. When these requests are being executed one by one, there is a possibility that one of the request's processing could throw an exception(OperationCanceledException) or let's say one of the batch requests is malformed, in which case we need to dispose the responses which were previously successfully created.
Discussed with Yao about this.
Repro:
1. Use the UnbufferedODataBatchHandler in ODataServiceSample and launch the service.
2. Open Fiddler and go to Composer tab and then the Raw tab.
3. Copy the attached file's content in the Raw tab and click Execute. __Note__ that the last embedded request in the batch request doesn't have Content headers to it. This was intentional.
4. You should see the request fail with 500 Internal Server Error.
5. Its not easy to find if dispose is called on responses, but I concluded this by have some trace statements in ODataResponseItem's dispose method.
DefaultODataBatchHandler:
```
public override async Task<HttpResponseMessage> ProcessBatchAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
cancellationToken.ThrowIfCancellationRequested();
ValidateRequest(request);
IList<ODataBatchRequestItem> subRequests = await ParseBatchRequestsAsync(request);
try
{
IList<ODataBatchResponseItem> responses = await ExecuteRequestMessagesAsync(subRequests, cancellationToken);
return await CreateResponseMessageAsync(responses, request);
}
finally
{
foreach (ODataBatchRequestItem subRequest in subRequests)
{
request.RegisterForDispose(subRequest.GetResourcesForDisposal());
request.RegisterForDispose(subRequest);
}
}
}
public virtual async Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(IEnumerable<ODataBatchRequestItem> requests, CancellationToken cancellationToken)
{
if (requests == null)
{
throw Error.ArgumentNull("requests");
}
cancellationToken.ThrowIfCancellationRequested();
IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();
foreach (ODataBatchRequestItem request in requests)
{
responses.Add(await request.SendRequestAsync(Invoker, cancellationToken));
}
return responses;
}
```
Comments: Fixed: https://aspnetwebstack.codeplex.com/SourceControl/changeset/1376452d6c6b52e37b89f2bb294af7e1e2662cdc
Discussed with Yao about this.
Repro:
1. Use the UnbufferedODataBatchHandler in ODataServiceSample and launch the service.
2. Open Fiddler and go to Composer tab and then the Raw tab.
3. Copy the attached file's content in the Raw tab and click Execute. __Note__ that the last embedded request in the batch request doesn't have Content headers to it. This was intentional.
4. You should see the request fail with 500 Internal Server Error.
5. Its not easy to find if dispose is called on responses, but I concluded this by have some trace statements in ODataResponseItem's dispose method.
DefaultODataBatchHandler:
```
public override async Task<HttpResponseMessage> ProcessBatchAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw Error.ArgumentNull("request");
}
cancellationToken.ThrowIfCancellationRequested();
ValidateRequest(request);
IList<ODataBatchRequestItem> subRequests = await ParseBatchRequestsAsync(request);
try
{
IList<ODataBatchResponseItem> responses = await ExecuteRequestMessagesAsync(subRequests, cancellationToken);
return await CreateResponseMessageAsync(responses, request);
}
finally
{
foreach (ODataBatchRequestItem subRequest in subRequests)
{
request.RegisterForDispose(subRequest.GetResourcesForDisposal());
request.RegisterForDispose(subRequest);
}
}
}
public virtual async Task<IList<ODataBatchResponseItem>> ExecuteRequestMessagesAsync(IEnumerable<ODataBatchRequestItem> requests, CancellationToken cancellationToken)
{
if (requests == null)
{
throw Error.ArgumentNull("requests");
}
cancellationToken.ThrowIfCancellationRequested();
IList<ODataBatchResponseItem> responses = new List<ODataBatchResponseItem>();
foreach (ODataBatchRequestItem request in requests)
{
responses.Add(await request.SendRequestAsync(Invoker, cancellationToken));
}
return responses;
}
```
Comments: Fixed: https://aspnetwebstack.codeplex.com/SourceControl/changeset/1376452d6c6b52e37b89f2bb294af7e1e2662cdc