This question is perhaps an idealogical one...
I have a requirement to create multiple records from an application calling an OData Web API through a service proxy.
At the moment, I'm doing this by invoking the post method multiple times (through a service proxy) and then calling save changes to batch the requests. Eg.
container.AddToColours(new Colour { Id = 1, Name = "Green" });
container.AddToColours(new Colour { Id = 2, Name = "Orange" });
container.SaveChanges(SaveChangesOptions.Batch);
The downside of this is that in my web api controller the post method is called twice and so generates two insert statements.
Now, if in my client did this
container.AddToColours(new List<Colour>() { new Colour { Id = 1, Name = "Green" }, new Colour { Id = 2, Name = "Orange" }});
container.SaveChanges(SaveChangesOptions.Batch);
Then only a single insert statement would be created that inserts both records at once.
The questions are;
Is this possible? (I'd probably end up having two POST methods - one for a single colour another to accept a list of colours).
Is this possible through a service proxy?
Does this break every/any rule in the OData/REST book?
Is this ethical?
Is this naughty?
Edit
Having revisited this I've been able to create a custom batch handler as suggested below. I'm hoping that this helps others. It saves all changes made in a batch in a single transaction so if one part of the batch fails then all parts fail. But, importantly, attempts are made to save for all operations in the batch regardless of previous successes or failures so error messages can be returned for operation.
I used the sample here as a basis:
http://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v3/ODataEFBatchSample/
But made the following changes:
Changed EntityFrameworkBatchHandler.ExecuteChangeSet method to:
private async Task ExecuteChangeSet(ChangeSetRequestItem changeSet, IList<ODataBatchResponseItem> responses, CancellationToken cancellation)
{
using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }, TransactionScopeAsyncFlowOption.Enabled))
{
responses.Add((ChangeSetResponseItem) await new ChangeSetBatchRequestItem(changeSet).SendRequestAsync(Invoker, cancellation));
if (responses.All(x => (x as ChangeSetResponseItem).Responses.All(y => y.IsSuccessStatusCode)))
{
scope.Complete();
}
}
}
I believe that this means that it is not tied to EF at all. I removed HttpRequestMessageExtensions.cs and created a new class, ChangeSetBatchRequestItem which is exactly the same as System.Web.OData.Batch.ChangeSetRequestItem except for the following method:
public override async Task<ODataBatchResponseItem> SendRequestAsync(HttpMessageInvoker invoker, CancellationToken cancellationToken)
{
if (invoker == null)
{
throw new ArgumentNullException("invoker");
}
Dictionary<string, string> contentIdToLocationMapping = new Dictionary<string, string>();
List<HttpResponseMessage> responses = new List<HttpResponseMessage>();
try
{
foreach (HttpRequestMessage request in Requests)
{
responses.Add(await SendMessageAsync(invoker, request, cancellationToken, contentIdToLocationMapping));
}
}
catch
{
DisposeResponses(responses);
throw;
}
return new ChangeSetResponseItem(responses);
}
And that's the lot.