Creating OData Service Using WCF DataService

The Open Data Protocol (OData) is a Web protocol for querying and updating data that provides a way to unlock your data and free it from silos that exist in applications today.

What is OData

This should be the first question if you're new to this term. Here is a brief introduction to it.

"The Open Data Protocol (OData) is a Web protocol for querying and updating data that provides a way to unlock your data and free it from silos that exist in applications today. OData does this by applying and building upon Web technologies such as HTTP, Atom Publishing Protocol (AtomPub) and JSONto provide access to information from a variety of applications, services, and stores. The protocol emerged from experiences implementing AtomPub clients and servers in a variety of products over the past several years. OData is being used to expose and access information from a variety of sources including, but not limited to, relational databases, file systems, content management systems and traditional Web sites."

Reference - http://www.odata.org

Want to know more, like What, Why and How? Visit: http://www.odata.org/introduction

Now after having gone through the specification of OData you'll can learn where it can fit your requirements when designing service-architecture aplications.

Let's avoid digging too deeply into these consideartions and return to the subject of how to create an OData service using a WCF DataService.

So go ahead and launch your VisualStudio 2010 (or 2012 if you have that one installed; it's available as a RC version at the time of writing this article).

Now create a web project or rather add a class library project. (Let's maintain a little layering and separation of business.)

The following are a simple step-by-step walkthrough, keeping in mind that someday some beginner (new to VS) might be having trouble with written instructions. The following shows creation of a Class Library:

WCFDtSrv1.jpg
Name it "Demo.Model" as we're creating a sample service and this will work as Model (Data) for our service.

Now go ahead and add a new item -> "ADO.Net Entity Data Model":

WCFDtSrv2.jpg

Name it "DemoModel.edmx". When you click Add, a popup will be waiting for your input. So either you can generate your entities from the database or you can just create an empty model which further can be used to generate your database. Make your choice.

But we're simply going to use the existing database as I already have a nice test database from Stackoverflow dumps.

WCFDtSrv3.jpg

Click "Next >" and get your connection string using "New Connection" and you're ready to click "Next >" again:

WCFDtSrv4.jpg

Select your database object to include into your Entities.

WCFDtSrv5.jpg

For creating the OData service we only need the Tables. So after clicking "Finish" your Entities diagram should look like this. Here's the important note: the relationship between the entities should be well-defined because the OData will be searching/locating your related entities based on your query (learn about URI Queries).

 WCFDtSrv6.jpg

We're close enough to finish the job more than 50% work is done to create the service. Forget the time when we used to define/create/use the client proxy and other configuration settings including creating the methods for every type of data required.

(Note:- This article will only show you how to get the data from the OData service. I can in the future write about Create, Delete and Edit using HTTP requests PUT, DELETE and POST.)

Now proceed to adding a new Web project (you should choose ASP.Net Empty website) from the templates; see:

WCFDtSrv7.jpg

Add the project reference Demo. Model to the newly added website.

WCFDtSrv8.jpg

Now add a new item in the web project i.e. WCF Data service.

WCFDtSrv9.jpg

Now open the code behind file of your service file and write this code:

1.            [JSONPSupportBehavior]     

2.           //[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]   //- for debugging

3.           public class Service : DataService<StackOverflow_DumpEntities>

4.           {         

5.               // This method is called only once to initialize service-wide policies.

6.               public static void InitializeService(DataServiceConfiguration config)

7.               {

8.                   config.SetEntitySetAccessRule("*"EntitySetRights.AllRead);             

9.                   //Set a reasonable paging site

10.                 config.SetEntitySetPageSize("*", 25);            

11.                 //config.UseVerboseErrors = true;  //- for debugging

12.                 config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;

13.             }

14.      

15.             /// <summary>

16.             /// Called when [start processing request]. Just to add some caching

17.             /// </summary>

18.             /// <param name="args">The args.</param>

19.             protected override void OnStartProcessingRequest(ProcessRequestArgs args)

20.             {

21.                 base.OnStartProcessingRequest(args);            

22.                 //Cache for a minute based on querystring

23.                 HttpContext context = HttpContext.Current;

24.                 HttpCachePolicy c = HttpContext.Current.Response.Cache;

25.                 c.SetCacheability(HttpCacheability.ServerAndPrivate);

26.                 c.SetExpires(HttpContext.Current.Timestamp.AddSeconds(60));
27.                 c.VaryByHeaders[
"Accept"] = true;
28.                 c.VaryByHeaders[
"Accept-Charset"] = true;
29.                 c.VaryByHeaders[
"Accept-Encoding"] = true;
30.                 c.VaryByParams[
"*"] = true;
31.             }
32.      
33.             
/// <summary>
34.             /// Sample custom method that you OData also supports
35.             /// </summary>
36.             /// <returns></returns>
37.             [WebGet]
38.             
public IQueryable<Post> GetPopularPosts()
39.             {
40.                 
var popularPosts =
41.                     (
from p in this.CurrentDataSource.Posts orderby p.ViewCount select p).Take(20);
42.                 
43.                 
return popularPosts;
44.             }
45.         }

Here we added some default configuration settings in the InitializeService() method. Note that I'm using the DataServiceProtocolVersion.V3 version. This is the latest version for the OData protocol in .Net, you can get it by installing the SP for WCF 5.0 (WCF Data Services 5.0 for OData V3) and replacing the references of System.Data.Services, System.Data.Services.Client to Microsoft.Data.Services, Microsoft.Data.Services.Client.

Otherwise it's not a big deal, you can still have your DataServiceProtocolVersion.V2 version and It works just fine.

Next in the above snippet I've added some cache support by overriding the method OnStartProcessingRequest().

Also I have not forgeten about the JSONPSupportBehavior attribute on the class. This is an open source library that enables your data service to have JSONP support.

First of all you should know why and what JSONP does over JSON. Keeping it simple and brief:

  • Cross domain support
     
  • Embedding the JSON inside the callback function call.

Hence.. We're done creating the service. Wondered!!? Don't be. Now let's run this project and see what we've got. If everything goes well then you'll get it running like this:

WCFDtSrv10.jpg

Now here's the interesting part.

Play with this service and see the OData power. Now you've seen that we didn't have any custom method implementation here and we'll be directly working with our entities using the URL and getting the returned format as Atom/JSON. :) .. Sounds cool?

If you have LINQPAD installed then launch it otherwise download and install it from here.

Do you know that LINQPAD supports the ODataServices. So we'll add a new connection and select WCF Data service as our datasource.

WCFDtSrv11.jpg

Now click next and enter your service URL. (Make sure the service that we just created is up and running)

WCFDtSrv12.jpg

The moment you click OK you'll see your all entities in the Connection panel.

WCFDtSrv13.jpg

Select your service in the Editor and start typing your LINQ queries:

WCFDtSrv14.jpg

I need the top 20 posts ordered by View count.. blah blah. So here is my LINQ query.

1.       (from p in Posts

2.       orderby p.ViewCount

3.       select p).Take(20)

Now run this with LINQPAD and see the results:

WCFDtSrv15.jpg

Similarly you can write any nested, complex LINQ queries on your ODataService.

Now here is the key of this. The root of the whole article:

WCFDtSrv16.jpg

This is the OData protocol URI conventions that helped you filter and order your data from entities. At the end of this article you've just created and API like Netflix, Twitter or another public API has.

Moreover, we used JSONPSupportBehavior so you can just add one more parameter to the above URL and enter it in a browser and go, as in:

WCFDtSrv17.jpg

In the next article we'll see how to consume this service in a simple plain HTML page using JavaScript. Keep an eye on the feeds. Stay in touch.