This is the third part of Building OData Service using Asp.Net Web API. The topics we’ll cover are:
- OData Introduction and Querying Existing OData Service – Part 1.
- Create read-only OData endpoint using Asp.Net Web API – Part 2.
- CRUD Operations on OData endpoint using Asp.Net Web API - Part 3 (This Post).
- Consuming OData Service using AngularJS and Breeze.js – Part 4 (Coming Soon).
CRUD Operations on OData endpoint using Asp.Net Web API
In this post We’ll add another OData controller which will support all the CRUD operations, as we talked before OData follows the conventions of HTTP and REST; so if we want to create a resources we’ll issue HTTP POST, if we want to delete a resource we’ll issue HTTP DELETE and so on. One more thing we want to add support for is partial updates (HTTP PATCH) which will be very efficient when we update certain properties on the entity, the request payload for PATCH will come on a key/value pair and will contain the properties changed only.
Step 1: Add OData Controller (Support for CRUD operations)
Let’s add a new Web API Controller which will handle all HTTP requests issued against the OData URI “/odata/Tutors”. To do this right-click on Controllers folder->Select Add->Name the controller “TutorsController” and choose “Empty API Controller” Template.
As we did before we have to derive our “TutorsController” from class “EntitySetController“, then we’ll add support for the Get() and GetEntityByKey(int key) methods and will implement creating a new Tutor; so when the client issue HTTP POST request to the URI “/odata/Tutors” the “CreateEntity(Tutor entity)” method will be responsible to handle the request. The code will look as the below:
public class TutorsController : EntitySetController<Tutor, int> { LearningContext ctx = new LearningContext(); [Queryable()] public override IQueryable<Tutor> Get() { return ctx.Tutors.AsQueryable(); } protected override Tutor GetEntityByKey(int key) { return ctx.Tutors.Find(key); } protected override int GetKey(Tutor entity) { return entity.Id; } protected override Tutor CreateEntity(Tutor entity) { Tutor insertedTutor = entity; insertedTutor.UserName = string.Format("{0}.{1}",entity.FirstName, entity.LastName); insertedTutor.Password = Helpers.RandomString(8); ctx.Tutors.Add(insertedTutor); ctx.SaveChanges(); return entity; } }
By looking at the code above you’ll notice that we overrided the CreateEntity() method which accepts a strongly typed Tutor entity, the nice thing here that once you override the CreateEntity() method the base class “EntitySetController” will be responsible of returning the right HTTP response message (201) and adding the correct location header for the created resource. To test this out let’s issue issue an HTTP POST request to URI “odata/Tutors”, the Post request will look as the below:
Step 2: Testing OData Prefer Header
By looking at the request above you will notice that status code is 201 created is returned as well the response body contains the newly created Tutor, but what if the client do not want to return a duplicate entity of the newly created Tutor from the server? The nice thing here that OData allow us to send Prefer header with request to indicate if we need to return the created entity back to the client. The default value for the header is “return-content” so if we tried to issue another POST request and pass the Prefer header with value “return-no-content” the server will create the resource and will return 204 no content response.
Step 3: Add Support for Resource Deletion
Adding support for delete is simple, we need to override method Delete(int key) so any HTTP DELETE request sent to the URI “/odata/Tutors “will be handled by this method, let’s implement this in the code below:
public override void Delete(int key) { var tutor = ctx.Tutors.Find(key); if (tutor == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } ctx.Tutors.Remove(tutor); ctx.SaveChanges(); }
To test this out we need to issue HTTP DELETE request to the URI “OData/Tutors(12)” and if the tutor exists the HTTP response will be 204 no content. What worth mentioning here that if we tried to delete a tutor which doesn’t exists then by looking at code on line 6 you will notice that I’m throwing HttpResponseException with status code 404 along with empty response body; nothing wrong about this but if you want to build OData compliant service then handling the errors should be done on a different way; to fix this we need to return an HttpResponseException with an object of type “Microsoft.Data.OData.ODataError” in the response body.
Step 4: Return Complaint OData Errors
To fix the issue above I’ll create a helper class which will return ODataError, we’ll use this class in different methods and for another controllers, so add new class named “Helpers” and paste the code below:
public static class Helpers { public static HttpResponseException ResourceNotFoundError(HttpRequestMessage request) { HttpResponseException httpException; HttpResponseMessage response; ODataError error; error = new ODataError { Message = "Resource Not Found - 404", ErrorCode = "NotFound" }; response = request.CreateResponse(HttpStatusCode.NotFound, error); httpException = new HttpResponseException(response); return httpException; } }
By looking at the code above you will notice that there is nothing special here, we only returning an object of ODataError in the response body, you can set the Message and ErrorCode properties for something meaningful for your OData service clients. Now insted of throwing the exception directly in the controller we’ll call this helper method as the code below:
if (tutor == null) { throw Helpers.ResourceNotFoundError(Request); }
Step 5: Add Support for Partial Updates (PATCH)
In cases we want to update 1 or 2 properties of a resource which has 30 properties; it will be very efficient if we are able to send only over the wire the properties which have been changed, as well it will be very efficient on the database level if we generated an update statement which contains the update fields only. To achieve this we need to override the method PatchEntity() as the code below:
protected override Tutor PatchEntity(int key, Delta<Tutor> patch) { var tutor = ctx.Tutors.Find(key); if (tutor == null) { throw Helpers.ResourceNotFoundError(Request); } patch.Patch(tutor); ctx.SaveChanges(); return tutor; }
As I mentioned before the changed properties will be in key/value form in the response body thanks to the Delta<T> OData class which makes it easy to perform partial updates on any entity, to test this out we’ll issue HTTP Patch request to the URI “/odata/Tutors(10)” where we will modify the LastName property only for this Tutor, request will be as the below:
Before we send the request Let’s open SQL Server Profile to monitor the database query which will be generated to update this Tutor, you will notice from the image below how efficient is using the Patch update when we want to modify certain properties on an entity, the query generated do an update only for the changed property.
In the next post we’ll see how we can consume this OData service from Single Page Application built using AngularJS and Breeze.js
Please drop your questions and comments on the comments section below.
Source code is available on GitHub.
The post Building OData Service using ASP.Net Web API Tutorial – Part 3 appeared first on Bit of Technology.