Practical differences between ASP.NET Web API and WCF Data Services

By Mirek on (tags: ASP.NET, OData, WCF Data Services, Web API, categories: infrastructure, code)

In this post I would like to point out the main practical differences between ASP.NET Web API and WCF Data Services (OData). For those who does not yet know too much about Web API there is a lot of articles and tutorial about this topic on the net. The starting point is of course Microsoft ASP.NET team site which provides all related information and a bunch of tutorials. Concerning WCF Data Services I recommend to start at MSDN site.

In simple sentence the Web API is a way for exposing resources (entities) via ASP.NET MVC 4 controllers. Each controller’s method must be decorated with appropriate Http method attribute (GET, POST, PUT, DELETE), so the Web API is a REST like service. Having this we can say that Web API is in fact nothing new, since in earlier version of ASP.NET MVC we could also decorate the controller’s method with HTTP methods attributes so they were accessed in a REST style way.

Let's now point out main practical differences between ASP.NET Web API and WCF Data Services

1. Time-to-market. Effort needed to create a service.

Creating WCF Data Service is extremely simple and fast. On the server side we just create a DataService generic class and provide our database context or any other type of entity context as a generic parameter to it. For instance creating following data service class

   1: public class Service : DataService<AppDbContext>
   2: {
   3:     public static void InitializeService(DataServiceConfiguration config)
   4:     {
   5:         config.SetEntitySetAccessRule("*", EntitySetRights.All);
   6:         config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
   7:     }
   8: }

and putting it in a Service.svc code behind file we got all entity sets defined in AppDbContext, exposed via OData service. So if our database context has following entity sets

   1: public class AppDbContext : DbContext
   2: {
   3:     public DbSet<Product> Products { get; set; }
   5:     public DbSet<Customer> Customers { get; set; }
   7:     public DbSet<Order> Orders { get; set; }
   8: }

then having WCF Data Service in place we can query products, customers and orders like this

   1: http://localhost/Service.svc/Products?$filter=Name eg 'Foo'
   2: http://localhost/Service.svc/Customers?orderby=Name
   3: http://localhost/Service.svc/Orders?$filter=CustomerId eq 3

In case of Web API we must add a separate controller for each entity type we want to expose and implement all necessary methods. So if we, for example, want to expose products we must add a ProductsControler (the naming convention here is <exposed entity set name>Controller)
   1: public class ProductsController : ApiController
   2: {
   3:   private AppDbContext ctx;
   5:   public ProductsController()
   6:   {            
   7:       ctx = new AppDbContext();
   8:   }
  10:   [HttpGet]
  11:   public IEnumerable<Product> GetProducts()
  12:   {
  13:       return ctx.Products;
  14:   }
  16:   [HttpGet]
  17:   public Product GetProduct(int id)
  18:   {
  19:       return ctx.Products.Find(id);
  20:   }
  22:   [HttpDelete]
  23:   public void DeleteProduct(int id)
  24:   {
  25:       var prod = ctx.Products.Find(id);
  26:       if (prod == null)
  27:           throw new HttpResponseException(System.Net.HttpStatusCode.NotFound);
  28:       ctx.Products.Remove(prod);
  29:       ctx.SaveChanges();
  30:   }
  32:   [HttpPut]
  33:   public void PutProduct(Product product)
  34:   {
  35:       var prod = ctx.Products.Find(product.Id);
  36:       if (prod == null)
  37:           throw new HttpResponseException(System.Net.HttpStatusCode.NotFound);
  38:       ctx.Products.Attach(product);
  39:       ctx.SaveChanges();
  40:   }
  42:   [HttpPost]
  43:   public void PostProduct(Product product)
  44:   {
  45:       ctx.Products.Add(product);
  46:       ctx.SaveChanges();
  47:   }
  48: }

That’s a lot of code for each entity type which we must implement and which in we can produce a bug!


2. Time-to-market. Client side implementation

In WCF Data Services the case is again simple. We just add an service reference, since the WCF Data Service is, as its name says, a WCF based service. Having service reference we can use it almost as a normal database context.

   1: WCFService.AppDbContext ctx = new WCFService.AppDbContext(new Uri("http://localhost/Service.svc"));
   2: var prod = ctx.Products.Where(p => p.Name == "Foo").SingleOrDefault();

ASP.NET Web API seems to be targeted to a portable devices and browser side script. We can query the api with javascript, so if we want to get the list of products we can do it as follows

   1: <script type="text/javascript">
   2:     $(document).ready(function () {
   3:         // Send an AJAX request
   4:         $.getJSON("api/products/",
   5:         function (data) {
   6:             // On success, 'data' contains a list of products.
   7:             $.each(data, function (key, val) {
   9:                 // Format the text to display.
  10:                 var str = val.Name + ': $' + val.Price;
  12:                 // Add a list item for the product.
  13:                 $('<li/>', { html: str })    
  14:                 .appendTo($('#products'));   
  15:             });
  16:         });
  17:     });
  18: </script>

Calling Web API programmatically is quite simple with use of new HttpClient class, which is available in ,net 4.5 framework. Fortunately the beta version of System.Net.Http, System.Net.Http.Formatting and System.Json are also available on NuGet and can be used without installing .net Framework  4.5.

   1: HttpClient client = new HttpClient();
   2: client.GetAsync(@"http://localhost:49330/api/Products").ContinueWith(
   3: (r) =>
   4: {
   5:     r.Result.Content.ReadAsAsync<List<Product>>().ContinueWith(
   6:     (body) =>
   7:     {
   8:         //a.Result is a List<Product>
   9:         Console.WriteLine(body.Result.Count().ToString());
  10:     });
  11: });

Above piece of code queries web api and gets the list of products. As you can see it is not as simple as in case of WCF DS call.

3. Queryable support

WCF Data Services natively supports queryable calls and quite rich filtering mechanism. We can use Linq to entities on the client side querying the data service context. The list of all supported Linq methods is available here.

The query filters can also be enabled on Web API resources. To do that we must returns the IQueryable in api controllers

   1: [HttpGet]
   2: public IQueryable<Product> GetProducts()
   3: {
   4:     return ctx.Products;
   5: }

Having that we can query Products with an WCF Data Service like syntax. However not all features are available here. For example we can not use expand to load related entities at once.

4. Extensibility

Adding or removing an entity type on Web API side requires to make modifications in controllers structure. In case of WCF DS such changes are automatically supported, the only thing to do is updating the service references on client side.

5. Interoperability

Since the Web API is Http based service it can be easily accessed by any kind of clients and platforms. WCF Data Services are also accessible via http, but the client side proxy generation is supported for .net applications only. That means if we want to consume the data service on non .net client we must operate on query strings, string filters and manually handle responses as we do it for Web API.

It seems that ASP.NET Web API is designed to be widely consumed by variety of clients, especially by client side scripts. WCF Data Services are, on the other hand, great thing to be used to communicate between client applications and backend servers. The main advantage of WCF Data Services is the easiness of exposing data to the external world.