Bit of Technology

  • Archive
  • About Me
    • Advertise
    • Disclaimer
  • Speaking
  • Contact

Building OData Service using ASP.Net Web API Tutorial – Part 3

April 16, 2014 By Taiseer Joudeh 27 Comments

Share this:

  • Twitter
  • LinkedIn
  • Reddit
  • Facebook
  • Email

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:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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:

web api odata crud

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:

C#
1
2
3
4
5
6
7
8
9
10
11
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:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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:

C#
1
2
3
4
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:

C#
1
2
3
4
5
6
7
8
9
10
11
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:

web api odata crud ODataPatchRequest

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.

ODataSqlProfiler

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.

Share this:

  • Twitter
  • LinkedIn
  • Reddit
  • Facebook
  • Email

Like this:

Like Loading...

Related Posts

  • Integrate Azure AD B2C with ASP.NET MVC Web App – Part 3
  • Secure ASP.NET Web API 2 using Azure AD B2C – Part 2
  • Azure Active Directory B2C Overview and Policies Management – Part 1
  • ASP.NET Identity 2.1 Accounts Confirmation, and Password Policy Configuration – Part 2
  • ASP.NET Identity 2.1 with ASP.NET Web API 2.2 (Accounts Management) – Part 1

Filed Under: ASP.Net Web API, OData, RESTful API, Web API Tutorial Tagged With: API, ASP.NET, OData, REST, RESTful, Tutorial, Web API, Web Service

Comments

  1. Slava says

    April 21, 2014 at 9:12 am

    Hi! Thanx for this post. But I have a problem. Do you know why the “entity” parameter in method CreateEntity is null, when I try to call service to create new entity?
    This is object that I post from javascript:
    {
    EmployeeBirth: “2014-04-24”
    EmployeeName: “qwe”
    EmployeeSalary: 111}
    This is DAL class:
    [Serializable]
    [DataContract]
    public class Employee
    {
    [DataMember]
    public int EmployeeId { get; set; }
    [DataMember]
    public string EmployeeName { get; set; }
    [DataMember]
    public DateTime EmployeeBirth { get; set; }
    [DataMember]
    public decimal EmployeeSalary { get; set; }
    }
    protected override Employee CreateEntity(Employee entity)
    {
    var empl = entity;//always null
    ///…///
    }

    Reply
    • Taiseer Joudeh says

      April 21, 2014 at 9:58 am

      Hello Salva,
      I recommend you to move back a step and try to issue POST request using fiddler, do not forget to set Content-Type header to “application/json” and your request body should look like the below:
      {
      “EmployeeBirth”: “2014-04-23”,
      “EmployeeName”: “qwe”,
      “EmployeeSalary”: 111
      }
      If the above work then ensure that you set the right Content-Type header when you issue POST request in JS, as well check your data format in JS object.

      Let me know if this helps.

      Reply
      • Slava says

        April 21, 2014 at 8:13 pm

        Hi, Taiseer! The problem was in JS object as you said. For successful passing a date property of any object to OData controller I need to do this:
        var date = new Date();
        date.setTime(Date.parse(employee.EmployeeBirth));
        employee.EmployeeBirth = date.toJSON();
        $http.post(‘odata/Employees’, employee); (angularjs thing)
        That’s the way the cookie crumbles! 🙂
        Thanx for answer!

        Reply
        • Taiseer Joudeh says

          April 21, 2014 at 8:15 pm

          You are welcome and thanks for sharing the answer 🙂

          Reply
  2. Amit says

    May 14, 2014 at 4:34 pm

    Hi Taiseer, Thanks for the wonderful post.

    A small query on POST.

    Taking your example, if a new course needs to be created using existing TutorID and SubjectID, how can this be acheived?
    Whenever SaveChanges method is called, it expects all foreign keys objects and creates a new records in Tutor and Subject table.

    In your web api tutorial, I came across with “parse” method inside ModelFactory class. Can anything similar be used here?

    Reply
  3. Jeremy Larcombe says

    May 22, 2014 at 6:50 pm

    Hi Taiseer, thanks for this tutorial, once again very clear and works well.

    I noticed this doesn’t use the repository/model concept from the WebAPI tutorial,. Are these still relevant when using OData? If so, how would they fit into your example?

    Many thanks

    Reply
    • Taiseer Joudeh says

      May 29, 2014 at 9:44 pm

      Hello Jeremy,

      Apologies for the late reply, well you can use the repository pattern with OData endpoints for sure. Mainly you can use the repository pattern for creating an abstraction between your domain objects and data layer. Hope this answers your question.

      Reply
  4. 寒駿 says

    July 25, 2014 at 8:43 pm

    Just want to say thank you for all your Web API tutorials. I like them so much and they are helpful. I prefer tutorials in text instead of videos. Keep up the good work! 😀

    Reply
    • Taiseer Joudeh says

      July 25, 2014 at 9:10 pm

      Thank you, really glad this was useful for you 🙂 subscribe to blog to keep updated.

      Reply
  5. gangareddy says

    August 1, 2014 at 3:00 pm

    Very good tutorial,look forward for consume OData service using AungularJs tutorial

    Reply
    • Taiseer Joudeh says

      August 1, 2014 at 3:17 pm

      Glad you liked it, will blog the last part soon.

      Reply
      • nick0696ick Jacobs says

        October 14, 2014 at 9:15 pm

        Just curious, did you ever do the final part about Angular & OData?

        Reply
        • Taiseer Joudeh says

          October 15, 2014 at 11:52 am

          Hi, not yet to be honest, but I’ll consider completing this tutorial soon. Too many things in the pipeline 🙁

          Reply
          • rob says

            March 9, 2015 at 6:20 am

            A helpfull tutorial (as are all of your tutorials). Looking forward to the AngularJS part 4.

  6. Khai Tran says

    December 12, 2014 at 5:21 pm

    Hi, sir. Thank you for all your awesome tutorials!! (:
    I just have a small question. Is there a way to add support for PATCH with traditional ApiController?

    Reply
    • Taiseer Joudeh says

      December 13, 2014 at 12:57 pm

      You are welcome Khai. I didn’t do it my self but yes sure there is way to implement patch, check this SO answer, hope this helps.

      Reply
      • Khai Tran says

        December 16, 2014 at 3:38 pm

        thank you, sir

        Reply
  7. Socratis21 says

    January 23, 2015 at 11:40 pm

    Any plan soon for Part 4 of this series. Thanks and great blog

    Reply
    • Taiseer Joudeh says

      January 24, 2015 at 1:18 am

      I will be lying if I said yes 🙂 I’m now working on another tutorial related to Angular, so keep tuned.

      Reply
  8. Kai Wen says

    February 10, 2015 at 7:12 am

    Hi Taiseer,

    Thanks so much for the demo. Great learning tool. I’m having a problem. When I compile to the solution no database is created. I changed to connection string to connect to my MSSQL 2012 database server and still noting is being created when I compile the solution. Any help would be appreciated.

    Regards,
    Kai Wen

    Reply
    • Taiseer Joudeh says

      February 10, 2015 at 3:56 pm

      Hi Kai, the DB will be created once you do first hit to the DB not compilation, like calling DbContext.Students.ToList() as well make sure that initialization strategy is set correctly.

      Reply
      • Kevin says

        February 15, 2015 at 10:53 pm

        Hi Taiseer,

        Not really sure what the problem is, but I’m still not able to run the app. I unzipped the demo code again and open the solution in VS2013 and I’m not able to get the site to run. I’m not making any code or web.config file changes. I made the Learning.Web project the start up site. Any help would be awesome. The code compiles with no errors.

        Regards,
        Kevin

        Reply
        • Taiseer Joudeh says

          February 16, 2015 at 10:14 am

          Hi Kevin,
          What do you mean the site is not running? You can not issue requests? Is there any errors after you execute the request?

          Reply
          • Kevin says

            February 17, 2015 at 12:17 am

            Hi Taiseer,

            I should have been more specific. Yes, I’m not able to issue any request. I don’t get any compile errors at all.

            Kevin

          • Kevin says

            February 17, 2015 at 12:36 am

            Hi Taiseer,

            Looks as if it a problem with the CreateEntity method found in the TutorsController.cs file.
            Getting an error on the following line of code:
            insertedTutor.UserName = string.Format(“{0}.{1}”,entity.FirstName, entity.LastName);

            I’m wondering if this is because the database is not being created.

            Kevin

          • Kevin says

            February 17, 2015 at 5:44 pm

            Hey Taiseer,

            I finally got the the code to work. I think my biggest problem was the database was not getting created for some reason. I changed the connection string to point to my local instance of MSSQL 2012 and everything works. Once I learn this I’m going to take a look at the password \ account demo you have posted. This is just want I need and what I’m trying to learn.

            Best Regards,
            Kevin

  9. Regan says

    March 18, 2015 at 7:35 pm

    eagerly waiting for part 4

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

About Taiseer

Husband, Father, Consultant @ MSFT, Life Time Learner... Read More…

Buy me a coffeeBuy me a coffee

Recent Posts

  • Invoking Dapr Services in Azure Container Apps using gRPC – Part 2
  • gRPC Communication In Azure Container Apps – Part 1
  • Azure Container Apps Volume Mounts using Azure Files – Part 12
  • Deploy Meilisearch into Azure Container Apps
  • Monitor Microservices App using Azure Managed Grafana

Blog Archives

Recent Posts

  • Invoking Dapr Services in Azure Container Apps using gRPC – Part 2
  • gRPC Communication In Azure Container Apps – Part 1
  • Azure Container Apps Volume Mounts using Azure Files – Part 12
  • Deploy Meilisearch into Azure Container Apps
  • Monitor Microservices App using Azure Managed Grafana

Tags

AJAX AngularJS API API Versioning ASP.NET ASP.NET 6 Authentication Autherization Server Azure Azure Active Directory B2C Azure AD B2C Azure Container Apps Azure Files Azure Storage Code First Dapr Dependency Injection Entity Framework ETag Foursquare API grpc IaC jQuery JSON JSON Web Tokens JWT KEDA Microservice Microsoft MVP Ninject OAuth OAuth 1.0 OData Redis Resource Server REST RESTful Single Page Applications SPA Token Authentication Tutorial Web API Web API 2 Web API Security Web Service

Search

Copyright © 2026 · eleven40 Pro Theme on Genesis Framework · WordPress · Log in

%d