Im trying to add the ability to my web api to batch calls (possibly in one transaction). My idea was to create a specific batch controller that would receive a number of API calls (in json, including Url, Http method, params) and then it would subsequently
perform each api calls then return the batch result. My first try above resulted in Resource not found problem which I suspect is related to routes not working. In that setup I would instantiate an HttpServer (using GlobalConfiguration.Configuration) and fire
of calls to SubmitRequestAsync.
Next option I tried was to use a DelegatingHandler and try to execute the calls in there. In this setup I'm getting a Method Not Allowed. The funny thing is if I let the batch call continue like a normal call to my previous btach controller action (emptied
ofc) and try that multiple times, the controller action only gets called once.
I'm guessing that MVC is caching/storing some request specific data in the bowels of the HttpRequestMessage, but I can't put my finger on it.
Has anyone else been down this road before or know of things to look out for when mimicking an request on the server?
aliostad is right, this goes against HTTP. A question I would ask is why do you need batching? If you have several different resources you're trying to consume, you might want to go down the composite route and have a composite resource in your API return
all the data you need. You would still keep the individual resources as separate endpoints where you would create/update/delete, and use the composite resource for reads only.
For example, say you have two endpoints, one for billing and another for a user's shipping preferences and you need to display both pieces of data on the screen to complete an order:
/users/123456789/billing
/users/123456789/shipping
What you can do is create a third resource, /orders/aaazzzb11249/payment, which could return both the billing and shipping representations from your other endpoints. What's nice about this is that you can use the first two endpoints elsewhere if that is the
only data that is needed at the time, and use the composite for scenarios where everything has to come together.
Maybe a bit more background would explain why I am after the batching behaviour. Our application has screens to manipulate resources, but the screens don't map one to on to the resources, so e.g. a single screen would do edits on multiple resources (all
of those are parent-child related). Up until now we have modeled this by having one big model per screen that is posted and then taken that apart at the server in order to update our data model. This worked, although for just a couple of screens we already
had an explosion of viewmodels which contained similar logic in javascript and in C#. Another disadvantage of this approach is that our API is totally useless for anything but our application.
My idea with the batching was to eliminate the serverside screen logic and have a 'simple' resource model on the server, which would make consuming our API for other applications easier.
As an example, where we would previous have this:
Model in javscript containing parent resource changes and child resource changes (like add one, delete another, update yet another) and have that send to the API where it would be bound to a C# model and then taken apart at the server to update our data
model.
It would be replaced with:
Model in javscript containing parent resource changes and child resource changes which post the individual resource changes to the server in a batch:
1. Update parent
2. Add child
3. Delete child
4. Update a child
As a bonus the batch could run in a single transaction.
Any obvious solution that I'm overlooking for this?
The main question here is whether the child models/entities have a life of their own or not. In DDD term, it is important to ask whether these children are
root aggregate or not.
For example, a car's wheel has no use outside the car. While a teacher outside school has a life of its own.
Breaking down all these are useful if they are to be used outside the parent.
The children in this case have no life outside their parent, so not root aggregate.
Seen from the UI, our current UI might group them in the same page for editing, but another UI design might allow editing just a single child, I'm hoping to support both options with a single API.
The key to design this sort of interaction is to try and work with resources of same type, e.g. batch actions only against wheels rather than batching actions against wheels and cars and drivers, etc. A sample request for batching actions against a "wheel"
resource - create a special controller on the wheels collection and POST a special data contract modeled explicitly for taking actions against wheels.
POST /cars/12/wheels/batch
<wheels-actions>
<to-create>
<wheel-data>...data ...</wheel-data>
<wheel-data>...data ...</wheel-data>
</to-create>
<to-update>
<wheel>...data ...</wheel>
<wheel>...data ...</wheel>
</to-update>
<to-delete>
<wheel-del> ID and Version </wheel-del>
<wheel-del> ID and Version </wheel-del>
</to-update>
</wheels-actions>
To be very orthodox on REST, the returned location header should be the wheels collection resource: "/cars/12/wheels", from where you can get the newly created representations and their links. However, it's more constructive to return in the POST response
some data regarding the newly created resources. But if the client would anyway refresh "/cars/12/wheels", then you can skip returning special data in the response, the location header would suffice.
Having a single payload you can now have the service handle everything in a single transaction.
If you need to do batching on resources of different types, just design an application-specific controller resource and specific data contracts and do POST on it. It'd be better not to do it though :)
bccoltof
Member
10 Points
5 Posts
Batching Web API calls
May 24, 2012 12:56 PM|LINK
Hi all,
Im trying to add the ability to my web api to batch calls (possibly in one transaction). My idea was to create a specific batch controller that would receive a number of API calls (in json, including Url, Http method, params) and then it would subsequently perform each api calls then return the batch result. My first try above resulted in Resource not found problem which I suspect is related to routes not working. In that setup I would instantiate an HttpServer (using GlobalConfiguration.Configuration) and fire of calls to SubmitRequestAsync.
Next option I tried was to use a DelegatingHandler and try to execute the calls in there. In this setup I'm getting a Method Not Allowed. The funny thing is if I let the batch call continue like a normal call to my previous btach controller action (emptied ofc) and try that multiple times, the controller action only gets called once.
I'm guessing that MVC is caching/storing some request specific data in the bowels of the HttpRequestMessage, but I can't put my finger on it.
Has anyone else been down this road before or know of things to look out for when mimicking an request on the server?
batch web
aliostad
Member
228 Points
55 Posts
Re: Batching Web API calls
May 24, 2012 02:43 PM|LINK
This is not a good idea. It is anti-HTTP.
HTTP is about request-response. You may schedule the back-end stuff as batching but not at HTTP level.
stevewarren
Member
85 Points
18 Posts
Re: Batching Web API calls
May 24, 2012 06:52 PM|LINK
aliostad is right, this goes against HTTP. A question I would ask is why do you need batching? If you have several different resources you're trying to consume, you might want to go down the composite route and have a composite resource in your API return all the data you need. You would still keep the individual resources as separate endpoints where you would create/update/delete, and use the composite resource for reads only.
For example, say you have two endpoints, one for billing and another for a user's shipping preferences and you need to display both pieces of data on the screen to complete an order:
/users/123456789/billing
/users/123456789/shipping
What you can do is create a third resource, /orders/aaazzzb11249/payment, which could return both the billing and shipping representations from your other endpoints. What's nice about this is that you can use the first two endpoints elsewhere if that is the only data that is needed at the time, and use the composite for scenarios where everything has to come together.
batch web
bccoltof
Member
10 Points
5 Posts
Re: Batching Web API calls
May 25, 2012 08:04 AM|LINK
Hi,
Maybe a bit more background would explain why I am after the batching behaviour. Our application has screens to manipulate resources, but the screens don't map one to on to the resources, so e.g. a single screen would do edits on multiple resources (all of those are parent-child related). Up until now we have modeled this by having one big model per screen that is posted and then taken that apart at the server in order to update our data model. This worked, although for just a couple of screens we already had an explosion of viewmodels which contained similar logic in javascript and in C#. Another disadvantage of this approach is that our API is totally useless for anything but our application.
My idea with the batching was to eliminate the serverside screen logic and have a 'simple' resource model on the server, which would make consuming our API for other applications easier.
As an example, where we would previous have this:
Model in javscript containing parent resource changes and child resource changes (like add one, delete another, update yet another) and have that send to the API where it would be bound to a C# model and then taken apart at the server to update our data model.
It would be replaced with:
Model in javscript containing parent resource changes and child resource changes which post the individual resource changes to the server in a batch:
1. Update parent
2. Add child
3. Delete child
4. Update a child
As a bonus the batch could run in a single transaction.
Any obvious solution that I'm overlooking for this?
aliostad
Member
228 Points
55 Posts
Re: Batching Web API calls
May 25, 2012 09:08 AM|LINK
Thanks for clarification.
The main question here is whether the child models/entities have a life of their own or not. In DDD term, it is important to ask whether these children are root aggregate or not.
For example, a car's wheel has no use outside the car. While a teacher outside school has a life of its own.
Breaking down all these are useful if they are to be used outside the parent.
So which case is it?
bccoltof
Member
10 Points
5 Posts
Re: Batching Web API calls
May 25, 2012 09:56 AM|LINK
The children in this case have no life outside their parent, so not root aggregate.
Seen from the UI, our current UI might group them in the same page for editing, but another UI design might allow editing just a single child, I'm hoping to support both options with a single API.
neaflo
Member
3 Points
13 Posts
Re: Batching Web API calls
May 26, 2012 12:39 PM|LINK
The key to design this sort of interaction is to try and work with resources of same type, e.g. batch actions only against wheels rather than batching actions against wheels and cars and drivers, etc. A sample request for batching actions against a "wheel" resource - create a special controller on the wheels collection and POST a special data contract modeled explicitly for taking actions against wheels.
POST /cars/12/wheels/batch
To be very orthodox on REST, the returned location header should be the wheels collection resource: "/cars/12/wheels", from where you can get the newly created representations and their links. However, it's more constructive to return in the POST response some data regarding the newly created resources. But if the client would anyway refresh "/cars/12/wheels", then you can skip returning special data in the response, the location header would suffice.
Having a single payload you can now have the service handle everything in a single transaction.
If you need to do batching on resources of different types, just design an application-specific controller resource and specific data contracts and do POST on it. It'd be better not to do it though :)