Introduction

OData represents Open Data Protocol is an OASIS standard initiated by Microsoft in the year 2007. This defines the best practices for the consumption of data and building with quarriable REST APIs.

The difference between Odata and REST API is, Odata is a specific protocol and REST is an architectural style and design pattern. Using REST we can request and get data using HTTP calls. OData is a technology that uses REST to consume and build data.

Expecting readers of this article having some knowledge on Odata query. But, to make a simple understanding. This protocol gives the power to the client to query the data on the database using a query string of REST API requests. It also helps to make more secure of the data by not exposing any database related information as well as limiting the data to the outside world.

In general, we need to build the Odata enabled web service using any popular programming language which takes care of building URLs, verbs, and their requests and expected responses accordingly. Now, at the client end to consume these Odata REST APIs, we need to have metadata that contains the request type as well response types to build the concrete classes or we need to create a service proxy class.

This article targeting to know how clients can consume existing Odata REST API using C#. So, let’s start.

Simple.Odata.Client is a library that supports all Odata protocol versions and can be installed from the NuGet package and supports both .NET Framework and .NET Core latest versions.

Initializing Client Instance

To communicate with Odata REST API, Simple.Odata.Client library has some predefined class called ODataClient. This class accepts service URL with some optional settings to do a seamless communication with Odata service.

var client = new ODataClient("https://services.odata.org/sferp/");

If you want to log all the requests and responses from this client object to the Console, we can have additional optional settings as below.

var client = new ODataClient(new ODataClientSettings("https://services.odata.org/sferp/")
{
    OnTrace = (x, y) => Console.WriteLine(string.Format(x, y))
});

Building the Typed Classes

This library doesn’t help you to build any Typed classes of the responses (tables/views) from the given service as we do with the entity framework. To build the typed DTO classes, we need to fetch the metadata from the configured Odata web service by appending $metadata at the end of the base URL as follows.

https://services.odata.org/sferp/$metadata

Metadata will be displayed in XML format, we need to identify the elements for the table and their columns with datatypes to create classes accordingly for each table.

Retrieving Data Collection

As we did with initializing the communication to the service and now we need to fetch some data from the service. For example, the following statement will fetch all the data in the table Articles with the help of Odata protocol.

var articles = await client
    .For<Article>()
    .FindEntriesAsync();

Here, the client is an object of the ODataClient class.

There is a problem with the above statement. As if this Article table having some millions of records, your HTTP call will block or returns timeout exception and you cannot achieve your requirement. To avoid such situations, we need to use annotations defined in this library.

Annotations will help to minimize the load on the network by limiting the records in a single fetch. Along with records it also sends the link to fetch the next set of records so that this can be fetched until all records got fetched from the Articles table.

var annotations = new ODataFeedAnnotations();
var article = await client
    .For<Article>()
    .FindEntriesAsync(annotations)
    .ToList();
while (annotations.NextPageLink != null)
{
    article.AddRange(await client
        .For<Article>()
        .FindEntriesAsync(annotations.NextPageLink, annotations)
  );
}

In the above code, the first call will fetch a set of 8 records (by default or it can decide as per network speed to avoid timeout exception) along with nextpagelink property. Just this property will set the URL of Odata web service to fetch the next page of records.

Include Associated Data Collections

Till now, we fetch the direct table but we can also have the requirement to fetch or include in terms of entity framework all their constraint key records as a response of the request. To achieve it, our library provided an Expand() method to declare all included tables information so that Odata API will associate it and map to the response object while sending the response.

var articles = await client
    .For<Article>()
    .Expand(x => new { x.Ratings, x.Comments })
    .FindEntryAsync();

In the above example, the system will fetch all article’s information along with its ratings and comments data as part of each article record by joining it accordingly at the web service end.

Authentication

Till now, we have understood how to configure and consume the data from the existing Odata service API. Now, in real-time to make their data secure, API should develop authenticate the request as well requested client. To do so, we need to generate a token based on the credentials shared by API services.

The following are the codebase to prepare the ODataClient object by generating the token based on given credentials.

private ODataClient GetODataClient()
        {
            try
            {
                string url = _config.GetSection("APIDetails:URL").Value;
                String username = _config.GetSection("APIDetails:UserName").Value;
                String password = _config.GetSection("APIDetails:Password").Value;

                String accessToken = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));

                var oDataClientSettings = new ODataClientSettings(new Uri(url));
                oDataClientSettings.BeforeRequest += delegate (HttpRequestMessage message)
                {
                    message.Headers.Add("Authorization", "Basic " + accessToken);
                };

                var client = new ODataClient(oDataClientSettings);

                Simple.OData.Client.V4Adapter.Reference();

                return client;
            }
            catch(Exception ex)
            {
                LogError("", $"Failed to connect API Services : {ex.Message}");
            }

            return null;
        }

In the above code, we are getting URL, Username, and password from appsettings.json file and then creating a Basic token by converting the combe username and password string. Once the token generated, we are sending this token in the Authorization header by using the ODataClientSettings object and creating the ODataClient object.

Once this ODataClient object created, we can request the data from OData web services as discussed above.

Hope this article helped to understand how C# based client application can be created and used to consume existing Odata API services.

 

Happy Coding !!!