Create OData Web Services and Use Filter Expressions

Create OData Web Services and Use Filter Expressions

Open Data Protocol (OData) exposes resources and allows clients to directly do queries over it. In the past when using SOAP, you expose some methods, that perform some operations over the data returned back to you the results, and if any client needs some specific data, then we changed our services by exposing down a new method that returns the necessary output. But with OData, also known as Resource Oriented Architecture, we can just expose the resources or the data and let the client make the queries depending on the requirements.

Here in this article I show how to create a simple OData Service and let the client make queries just like SQL queries over it to retrieve the required data. Queries can be made by just specifying the filter expressions in the URL only.

So to start with I have created a WCF Service Application.

WCF Service Application

After creating, we need to create a Data Service, that is done by just the following simple steps: right-click on the WCF application and add a WCF Data Service as shown in the screenshot below.

WCF Data Service

As soon as it is done, you will see the generated service file:

public class SampleWCFDataService : DataService< /* TODO: put your data source class name here */ >

{

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

    public static void InitializeService(DataServiceConfiguration config)

    {

       // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.

       // Examples:

       // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);

       // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);

       config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;

    }

}

I have highlighted it in yellow. To get the service ready to be used we need to specify the Data Source Class, this can be done by simply providing an Entity Framework Model for a specific database. So what this Data Source class should support is the IQueryable properties over the exposed entities. The Entity Framework model exposes all your entities as IQueryable properties. It allows directly exposes your tables and lets the client make queries over it using Filter Expressions.

But for the sake simplicity I am not creating an Entity Framework model and will be creating a Data context class that has IQueryable properties.

So let's look into how to create a DataContext Class, before that we need to create some entities (Data Service Entities) that will be exposed as IQueryable entities via this DataContext class, here I will be creating a single entity, User:
 

[DataServiceKey("Id")]

[DataServiceEntity]

public class User

{

    public int Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

}


An Attribute has been added specifying the User class as the Data Service Entity. Now we will create a context class:

public class SampleContext

{

    #region Static Constructor

 

    /// <summary>

    /// In this static constructor i am creating some data which will be filled into users list, the purpose of creating static constructor is that it will be executed once

    /// on the creating of very first object and fills in some data into the users list which we can use to run through the demo.

    /// </summary>

    static SampleContext()

    {

        users = new List<User>();

 

        User user1 = new User();

        user1.Id = 1;

        user1.Name = "Abhishek1";

        user1.Age = 20;

 

        User user2 = new User();

        user2.Id = 2;

        user2.Name = "Abhishek2";

        user2.Age = 30;

 

        User user3 = new User();

        user3.Id = 3;

        user3.Name = "Abhishek3";

        user3.Age = 40;

 

        users.Add(user1);

        users.Add(user2);

        users.Add(user3);

    }

 

    #endregion

    #region Private Members

    static IList<User> users;

    #endregion

    #region Public Methods

    public IQueryable<User> Users

    {

        get

        {

            return users.AsQueryable();

        }

    }

    #endregion

}

 

Now you can see that I have provided the Data Source for the Data Service we created earlier to this context class:

WCF Context Class

With this we are ready to use this service:

XML File

So above you can see that the Data Service is working, and the format in which it is displayed above is the Atom pub format. By default it doesn't expose the Data Context we created and set in the Data Source in the service. To do that what we need to do is in the service code just uncomment the code highlighted in yellow. What this code does is, it defines the Entity access rule for the entities (that all entities must expose) so what we are doing is, we are exposing the entity Users below and are setting the Access level to Read, so we are only allowing Read operations.

public class SampleWCFDataService : DataService<SampleContext>

{

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

    public static void InitializeService(DataServiceConfiguration config)

    {

        // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.

        // Examples:

        config.SetEntitySetAccessRule("Users", EntitySetRights.AllRead);

        // config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);

        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;

    }

}

Now if you access the Service again you will see something as in the following:
 

Access the Service

So here we get a new collection of users, that we can use in the URL (the same as the REST format) to get the users collection.

 

Users Collection

So here we are getting the Users Collection in the Atom PUB format shown above. So what's the use of this is we can make a request to this URL (http://localhost/SampleWCFDataService.svc/Users) from a Web Page or a JavaScript file (data can be returned in JSON format) and use the output, in other words a Users list in the Web Page.

If we understand the format above then we can see that it already provides the things to determine how to query down the Data Source or can use a filter expressions in it. Just for the sake of understanding I am formatting the output above in XML Format.
 

<?xml version="1.0" encoding="utf-8"?>

<feed xml:base="http://localhost:51937/SampleWCFDataService.svc/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">

    <id>http://localhost:51937/SampleWCFDataService.svc/Users</id>

    <title type="text">Users</title>

    <updated>2013-10-08T14:44:06Z</updated>

    <link rel="self" title="Users" href="Users" />

    <entry>

        <id>http://localhost:51937/SampleWCFDataService.svc/Users(1)</id>

        <category term="SampleService.User" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

        <link rel="edit" title="User" href="Users(1)" />

        <title />

        <updated>2013-10-08T14:44:06Z</updated>

        <author>

            <name />         

    </author>

        <content type="application/xml">

            <m:properties>

                <d:Id m:type="Edm.Int32">1</d:Id>

                <d:Name>Abhishek1</d:Name>

                <d:Age m:type="Edm.Int32">20</d:Age>             

      </m:properties>         

    </content>     

  </entry>

    <entry>

        <id>http://localhost:51937/SampleWCFDataService.svc/Users(2)</id>

        <category term="SampleService.User" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

        <link rel="edit" title="User" href="Users(2)" />

        <title />

        <updated>2013-10-08T14:44:06Z</updated>

        <author>

            <name />

         

    </author>

        <content type="application/xml">

            <m:properties>

                <d:Id m:type="Edm.Int32">2</d:Id>

                <d:Name>Abhishek2</d:Name>

                <d:Age m:type="Edm.Int32">30</d:Age>             

      </m:properties>         

    </content>     

  </entry>

    <entry>

        <id>http://localhost:51937/SampleWCFDataService.svc/Users(3)</id>

        <category term="SampleService.User" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

        <link rel="edit" title="User" href="Users(3)" />

        <title />

        <updated>2013-10-08T14:44:06Z</updated>

        <author>

            <name />       

    </author>

        <content type="application/xml">

            <m:properties>

                <d:Id m:type="Edm.Int32">3</d:Id>

                <d:Name>Abhishek3</d:Name>

                <d:Age m:type="Edm.Int32">40</d:Age>             

      </m:properties>         

    </content>     

  </entry>

</feed>
 
So if we enter the preceding highlighted command directly into the Address Bar we get something as in the following that is the very first user in the list:
 

<?xml version="1.0" encoding="utf-8"?>

<entry xml:base="http://localhost:51937/SampleWCFDataService.svc/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">

    <id>http://localhost:51937/SampleWCFDataService.svc/Users(1)</id>

    <category term="SampleService.User" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

    <link rel="edit" title="User" href="Users(1)" />

    <title />

    <updated>2013-10-08T14:53:39Z</updated>

    <author>

        <name />     

  </author>

    <content type="application/xml">

        <m:properties>

            <d:Id m:type="Edm.Int32">1</d:Id>

            <d:Name>Abhishek1</d:Name>

            <d:Age m:type="Edm.Int32">20</d:Age>         

    </m:properties>     

  </content>

</entry>


This is how the service works and we can actually directly consume the data into the Web Page where we need to. Now we will look into how to use a filter expression directly while accessing the Data Service and retrieve the results. There are many filter expressions available:

  • And
  • Or
  • Less Than(equal to)
  • Greater Than(equal to)
  • Not Equal
  • Ends with
  • Starts With
  • Length
  • Index Of
  • Replace
  • And many more…

To understand how to actually use them, consider the preceding scenario in which we are getting the Users collection. To get all the users whose age is greater than 22 we can simply make a filter query (http://localhost/SampleWCFDataService.svc/Users?$filter=Age gt 22) and get the results.

Users collection with Filter Query

<?xml version="1.0" encoding="utf-8"?>

<feed xml:base="http://localhost:51937/SampleWCFDataService.svc/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">

    <id>http://localhost:51937/SampleWCFDataService.svc/Users</id>

    <title type="text">Users</title>

    <updated>2013-10-08T15:13:42Z</updated>

    <link rel="self" title="Users" href="Users" />

    <entry>

        <id>http://localhost:51937/SampleWCFDataService.svc/Users(2)</id>

        <category term="SampleService.User" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

        <link rel="edit" title="User" href="Users(2)" />

        <title />

        <updated>2013-10-08T15:13:42Z</updated>

        <author>

            <name />         

    </author>

        <content type="application/xml">

            <m:properties>

                <d:Id m:type="Edm.Int32">2</d:Id>

                <d:Name>Abhishek2</d:Name>

                <d:Age m:type="Edm.Int32">30</d:Age>             

      </m:properties>         

    </content>     

  </entry>

    <entry>

        <id>http://localhost:51937/SampleWCFDataService.svc/Users(3)</id>

        <category term="SampleService.User" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

        <link rel="edit" title="User" href="Users(3)" />

        <title />

        <updated>2013-10-08T15:13:42Z</updated>

        <author>

            <name />         

    </author>

        <content type="application/xml">

            <m:properties>

                <d:Id m:type="Edm.Int32">3</d:Id>

                <d:Name>Abhishek3</d:Name>

                <d:Age m:type="Edm.Int32">40</d:Age>             

      </m:properties>         

    </content>     

  </entry>

</feed>

So as it should be, it is returning only two users, now let's make it greater than 22 and less than 35 (http://localhost/SampleWCFDataService.svc/Users?$filter=Age gt 22 and Age lt 35) that should return only one result. Now here we have seen the use of both greater than, less than and the "and" expressions.

Similarly you can use the other available filter expressions. You can also download the sample application to see how it all works.