Advanced OData Tutorial with Simple.OData.Client

This article describes advanced OData scenario, both for the most recent OData protocol (V4) and earlier versions. The article was inspired by the Advanced OData Tutorial article published at the odata.org. The original article describes the HTTP communication level: it shows how to build URLs, which verbs to use and what response to expect. Most of developers prefer using some kind of library to encapsulate OData HTTP communication, and we are going to study how to solve the advanced OData scenarios using Simple.OData.Client. This library supports all OData protocol versions and can be installed as a NuGet package for either OData V1-3, V4 or as version-agnostic client. All library versions are packaged as Portable Class Library with support for .NET 4.x, Windows Store, Windows Phone 8, Silverlight 5, Xamarin iOS and Xamarin Android platforms.

Creating client instance

To communicate with OData service we need an instance of ODataClient. It can be created either from its URL string or using ODataClientSettings. Here’s the simplest version:

var client = new ODataClient("http://services.odata.org/V4/TripPinServiceRW/");

And here is a slightly more complicated example where we instruct the client not to raise exceptions on 404 error and trace all requests so we can check the HTTP communication:

var client = new ODataClient(new ODataClientSettings("http://services.odata.org/V4/TripPinServiceRW/")
{
    IgnoreResourceNotFoundException = true,
    OnTrace = (x, y) => Console.WriteLine(string.Format(x, y)),
});

In the examples above we used an address of Microsoft’s TripPin sample service. This is an OData V4 service that exposes most of the essential OData features, and this is the one we will be using through this article.

Choosing API flavor

Despite the word “Simple” in its title, Simple.OData.Client actually supports three API flavors: typed, dynamic and untyped.

Typed API is what most developers expect when writing C# code. OData protocol is about exposing domain-specific resources over HTTP, and if we provide a set of corresponding C# entity classes, the client will perform necessary conversions of both OData query strings and query results. At the present time Simple.OData.Client doesn’t include entity generation tool, so you will have to write entity classes yourself. Note that classes generated by WCF Data Services proxy generator are not compatible with Simple.OData.Client because they require use of context, and Simple.OData.Client is as stateless as REST protocol itself.

Dynamic API is works without entity classes, using dynamic C# syntax. It is best for OData service exploration, when all you need to consume OData feed is just a few lines of code. Moreover, the syntax of this code will be very similar to the syntax of typed API (and in fact it is the same API, just different overloads), so you can later easily convert your dynamic code to become typed.

Finally, untyped API is what the client uses internally where all data are stored in collections of IDictionary. This syntax is also exposed and fits explorarion and ad-hoc access scenarios. In our examples we will be using typed API flavors but will show for couple of examples its dynamic counterpart. They are interchangeable.

Retrieving top-level collections

To retrieve entries from an OData service collection, we only need to know the collection name. If the collection name matches the name of our entity type (either in singular or plural form), the client will manage to figure out the name of the corresponding OData resource. Otherwise we can send the name of the collection with the method call. Here how we can retrieve the entries from People collection:

var people = await client
    .For<Person>()
    .FindEntriesAsync(annotations);

For an entry-level introduction to OData there probably wouldn’t be more to say about how to retrieve top-level OData collection elements, but this is an advanced tutorial, so we need to complicate the things. If you checked the content of the sample TripPin database, you would discover 20 items in People collection. And if you check the count of items returned by code above, you will find that it’s only 8. So where is the rest?

By inspecting the HTTP traffic we can discover some additional data returned together with People items, among them so called OData annotations. Annotations supply extra information about the data being retrieved like links to endpoints that can be accessed to perform further operations with the response data. But sometimes these links are also essential to fully perform the current operation. To prevent the service from being overloaded by unconstrained requests (e.g. requesting all data from a particular collection), the service may issue its own constraints on such requests together with a hint about how to retrieve the rest of data. Simple.OData.Client can handle such annotations, so the code to obtain all People entries will look like this:

    var annotations = new ODataFeedAnnotations();
    var people = await client
        .For<Person>()
        .FindEntriesAsync(annotations)
        .ToList();

    while (annotations.NextPageLink != null)
    {
        people.AddRange(await _client
            .For<Person>()
            .FindEntriesAsync(annotations.NextPageLink, annotations));
    }

Expanding associated collections

If you look at TripPin service metadata, you will see that People collection refer to other collections: emails, addresses, friends, trips and photos. OData protocol is lazy, it doesn’t fetch all associated data unless explicitly requested. Here how we can expand People key lookup result to include Trips and Friends:

    var person = await client
        .For<Person>()
        .Key("russellwhyte")
        .Expand(x => new { x.Trips, x.Friends })
        .FindEntryAsync();

In this example we expanded the result containing a single person data, but expansions can also be applied to requests for multiple items.

In case you have opted for a dynamic syntax, here’s the corresponding code:

    var x = ODataDynamic.Expression;
    var person = await client
        .For(x.People)
        .Key("russellwhyte")
        .Expand(x.Trips, x.Friends)
        .FindEntryAsync();

As you can see, the dynamic syntax is very similar and differs in the following:

  • You need to define somewhere in the code an instance of OData dynamic expression (ODataDynamic.Expression). You don’t need to define it every time you make a call, actually it’s sufficient to define it once for the whole application. This variable is an intermediate placeholder for expressions.
  • Once a variable of ODataDynamic.Expression is instantiated, you can use it anywhere you would use any typed expression, typically generic type specifiers and lambda expressions.

Navigating to associated collections

Results expansions enrich results with associated information, but what if we only need that associated information and are not interested in the root level data. Continuing exploration of People, we will now lookup a person, navigate to one of his trips and then fetch all plan items registered on that trip. Here’s how this can be achieved:

    var planItems = await client
        .For<Person>()
        .Key("russellwhyte")
        .NavigateTo(x => x.Trips)
        .Key(1003)
        .NavigateTo(x => x.PlanItems)
        .FindEntriesAsync();

Note that we had to call NavigateTo method twice: first to advance from the People entry to Trips collection (thus abandoning People details like if we started from Trips collection), and then advancing to PlanItems collection (again, abandoning Trips details). The result will contain a collection of PlanItems data, just like if it was a top-level collection, only that if will contain only plan items for the specific trip of the specific person.

Using Any and All clauses

Now that we know how to retrieve OData collections on different levels, let’s have a look at how we can filter them using Any and All quantors. Suppose we want to fetch people information that includes their trips but only if the trip budget was over $10000. Here’s what we do:

    var flights = await client
        .For<Person>()
        .Filter(x => x.Trips
            .All(y => y.Budget > 10000d))
        .Expand(x => x.Trips)
        .FindEntriesAsync();

To make the search criteria more complex, let’s include both Any and All clauses. We will now search for people with trips all of them would contain at least one plan item with duration longer than 4 hours:

    var duration = TimeSpan.FromHours(4);
    var flights = await client
        .For<Person>()
        .Filter(x => x.Trips
            .All(y => y.PlanItems
                .Any(z => z.Duration < duration)))
        .FindEntriesAsync();

And here’s the same operation written using dynamic C# syntax:

    var x = ODataDynamic.Expression;
    var duration = TimeSpan.FromHours(4);
    var flights = await client
        .For(x.People)
        .Filter(x.Trips
            .All(x.PlanItems
                .Any(x.Duration < duration)))
        .FindEntriesAsync();

Note that we didn’t have to use different variables in different lambda-expressions: the “x” dynamic expression variable is recycled after each use so it can be used again in the next clause.

Querying singletons

Singleton concept is a newcomer to OData. What if one particular element of the OData collection has a distinct role of being the one and only when it comes to certain quality? A company that runs a Web shop may be registered in the Company database together with its clients, but it might be convenient to expose its details in a way that makes it easy to refer to that company without hard-coding its ID in the client application. This can achieved using OData singletons.

A singleton in OData is a top-level collection that contains only one item. You can’t search for items within the singleton, it doesn’t make sense. You can only grab that only record it contains.

    var person = await client
        .For<Person>("Me")
        .FindEntryAsync();

Note that we only had to specify a different collection name (“Me”) and the command doesn’t have a query filter or key, otherwise it looks just like a request to People collection. And the singleton quality only applies to the top-level element – the “Me” person may refer to other collections that can be navigated in the same way like demonstrated earlier.

Subclassing search criteria and results

Subclassing is one of the less known features of OData, but it’s quite powerful. In addition to grouping data into different collections, it is also possible to categorize them within each collection as being data of different subtype of the collection type. Moreover, it is also possible to search for data of the specific type. Collections of subclassed data are called derived collections.

Suppose we want to retrieve the given persons’s plan items but we also want to restrict the plan items by only flights and include flight details not exposed by the PlanItem collection. Simple.OData.Client has a clause As that serves exactly this purpose:

    var flights = await client
        .For<Person>()
        .Key("russellwhyte")
        .NavigateTo(x => x.Trips)
        .Key(1003)
        .NavigateTo(x => x.PlanItems)
        .As<Flight>()
        .FindEntriesAsync();

Flight definition includes additional fields like FlightNumber and Airline that don’t exist in PlanItem definition, and these extra properties will be retrieved by the client as long as it uses As clause. And once we navigate to the subclass, we can write query expressions that include fields defined by that subclass, like in the example below where we lookup for a flight with the specific flight number among the given person’s flights:

    var flights = await client
        .For<Person>()
        .Key("russellwhyte")
        .NavigateTo(x => x.Trips)
        .Key(1003)
        .NavigateTo(x => x.PlanItems)
        .As<Flight>()
        .Filter(x => x.FlightNumber == "FM1930")
        .FindEntriesAsync();

Modifying derived collection entries

Ability to specify derived collections in search criteria is quite useful, but there must also be a way to create, updated and delete subclassed data. Deletion doesn’t require additional functionality because OData resources are always deleted by key. They are also updated by key, but updating derived collections need special attention since a client should be able to send values of the properties that are only defined in the subclass. The same goes for insertion where a client needs to send derived collection property values.

Here is the syntax of the statement that inserts into person’s PlanItems an entry of a type Event. Event type is derived from PlanItem and adds a couple of properties (Descrption and OccursAt).

    var tripEvent = client
        .For<Person>()
        .Key("russellwhyte")
        .NavigateTo<Trip>()
        .Key(1003)
        .NavigateTo(x => x.PlanItems)
        .As<Event>()
        .Set(CreateEventDetails())
        .InsertEntryAsync();

To make the code easier to read, I extracted the method CreateEventDetails that initialized properties of the Event instance:

    private Event CreateEventDetails()
    {
        return new Event
        {
            ConfirmationCode = "4372899DD",
            Description = "Client Meeting",
            Duration = TimeSpan.FromHours(3),
            EndsAt = DateTimeOffset.Parse("2014-06-01T23:11:17.5479185-07:00"),
            OccursAt = new EventLocation()
            {
                Address = "100 Church Street, 8th Floor, Manhattan, 10007",
                BuildingInfo = "Regus Business Center",
                City = new Location.LocationCity()
                {
                    CountryRegion = "United States",
                    Name = "New York City",
                    Region = "New York",
                }
            },
            PlanItemId = 33,
            StartsAt = DateTimeOffset.Parse("2014-05-25T23:11:17.5459178-07:00"),
        };
    }

Updating the event uses the same technique:

    var tripEvent = client
        .For<Person>()
        .Key("russellwhyte")
        .NavigateTo<Trip>()
        .Key(1003)
        .NavigateTo(x => x.PlanItems)
        .As<Event>()
        .Key(33)
        .Set(new { Description = "This is a new description" })
        .UpdateEntryAsync();

Note that the reason we need to navigate to PlanItems collection via Person is that plan items don’t have a top-level collection – they are contained in their respective person’s details. Otherwise we could start right from PlanItems.

Executing functions and actions

OData has supported so called service operations for a long time, but the version 4 of OData protocol has a clear distinction between functions and actions. Functions don’t have side effects and are usually executed using GET command (although the service may support sending function arguments as POST content). Actions has side effect and following REST principles are executed using POST. Both function and actions can be bounded and unbounded which means that they are applied to a collection or the whole service respectively.

Here’s how Simple.OData.Client can execute unbound function GetNearestAirport that takes geolocation as an input and returns the closest airport to the specified coordinates:

    var airport = await client
        .Unbound<Airport>()
        .Function("GetNearestAirport")
        .Set(new { lat = 100d, lon = 100d })
        .ExecuteAsSingleAsync();

And this is an example for unbound action:

    await client
        .Unbound()
        .Action("ResetDataSource")
        .ExecuteAsync();

To execute a bound function or action, we need to specify the collection. In the following example we execute an action ShareTrip that shares the person’s trip information with another person.

    await client
        .For<Person>()
        .Key("russellwhyte")
        .Action("ShareTrip")
        .Set(new { userName = "scottketchum", tripId = 1003 })
        .ExecuteAsSingleAsync();

Bounded function and actions don’t need to be applied to top-level collection. The OData service metadata specifies only the type the function or action is bound to, not an entity set.

Executing OData batch operations

Last but not least we will show how to use Simple.OData.Client with batch operations. Developers sometimes think of OData batches as transactions, but this is not correct. Batches in OData are used to optimize HTTP traffic and reduce the number of roundtrips. You can always cancel the batch while it is being built (and it will cancel all its operations) but once the batch is sent to the server, you can not ensure all-or-nothing behavior. The OData service executes batch operations one by one without ability to rollback earlier executed operations in case of a failure. But that’s not all: there is no guarantee that the outcome of the current operation will be available for the next one, neither there is a guarantee that it will not. It all depends on how the service works, so when executing a series of OData operations as a batch, don’t put any transactional semantics in it without checking the service implementation.

The following example executes a batch that consists of three operations: retrieving all airlines, creating a new airline and retrieving all airlines again. If you run this batch on the TripPin service example, you will find that although a new airline is created successfully, it will not be included in the list of airlines returned by the next batch operation call. This is not an error, just a service behavior, as mentioned above.

    IEnumerable<Airline> airlines1 = null;
    IEnumerable<Airline> airlines2 = null;

    var batch = new ODataBatch(client);
    batch += async c => airlines1 = await c
       .For<Airline>()
       .FindEntriesAsync();
    batch += c => c
       .For<Airline>()
       .Set(new Airline() { AirlineCode = "TT", Name = "Test Airline"})
       .InsertEntryAsync(false);
    batch += async c => airlines2 = await c
       .For<Airline>()
       .FindEntriesAsync();
    await batch.ExecuteAsync();

Also note that you only need to await batch operations in case you need to get hold of its result. The operation in the middle (insertion) doesn’t need to store its result, so it doesn’t need to be awaited.

Advertisements

Resolving FileNotFoundException when using Simple.OData.Client in iOS applications

Several users of Simple.OData.Client 4.0 pre-release reported about an error when using the library on iOS platform. An attempt to send an OData request resulted in FileNotFoundException with the error message about the V3 or V4 adapter, e.g.:

System.IO.FileNotFoundException: Could not load file or assembly 'Simple.OData.Client.V3.Adapter' or one of its dependencies.

This error occurs only on iOS platform, and it is caused by application size optimization performed by the iOS linker. The linker scans applications dependencies and strips out all unused assemblies. Or should I say – assemblies it suspects to be unused. To reduce memory footprint, Simple.OData.Client 4.0 loads only assemblies that are required by the selected version of OData protocol, and the root adapter assembly is loaded by reflection making the iOS linker believe that it’s unused.

This is in fact well-documented behavior, and there are several workarounds. Some of them requires marking code to be preserved with special attributes, but Simple.OData.Client is a packaged as a portable class library, so it can’t have dependencies on platform-specific definitions. An alternative is to include in the main application assembly a single line of code referencing the OData adapter assembly. Here’s how you do it if your application uses OData services over V3 protocol:

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

And of the code for V4 protocol is similar:

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

Note that this code does nothing. Absolutely nothing. So you can place it anywhere within the iOS application assembly. And it’s only iOS apps that need it. Android, Windows Phone and desktop applications will run fine without an explicit adapter reference.

Breaking change in Simple.OData.Client 4.0: OData batch processing

One month ago I announced a new major release of Simple.OData.Client, and in this post I would like to describe one of its few breaking changes: executing OData batch operations.

Earlier versions of Simple.OData.Client had a simple but somewhat naive (and incomplete) support for OData batches. Here’s an example:

using (var batch = new ODataBatch(serviceUri))
{
    var client = new ODataClient(batch);
    await client
        .For&lt;Product&gt;()
        .Set(new { ProductName = "Test3", UnitPrice = 21m })
        .InsertEntryAsync(false);
    await client
        .For&lt;Product&gt;()
        .Set(new { ProductName = "Test4", UnitPrice = 22m })
        .InsertEntryAsync(false);
    batch.Complete();
}

This example works fine because it consists only of insert operations and don’t return anything back. While data modification requests represent a primary use of batch operations, OData batch protocol supports data retrieval (GET) operations that can be mixed with POST/PUT/PATCH/DELETE requests in a single batch. And GET requests didn’t work with earlier Simple.OData.Client versions because its batch API was modeled after SQL transaction API that executes individual statements as they come with possibility to rollback them back. OData batch is a different beast: it accumulates multiple operations and sends them at once as a single HTTP POST request once all operations are written into a batch message. Such behavior prevents responses from individual batch operations to be assigned to local variables before the batch is executed. So the code above succeeds in following “tell don’t ask” principle but it will fail if there is a need to ask about something within a batch.

Batch API in Simple.OData.Client 4 is completely rewritten to properly reflect OData batch behavior. It uses a concept of promises which is implemented using C# lambda expressions. The batch operations are recorded as a sequence of Func delegates having signature Func<IODataClient, Task>. So the statement above will look like this:

var batch = new ODataBatch(serviceUri);
batch += c =&gt; c
    .For&lt;Product&gt;()
    .Set(new { ProductName = "Test3", UnitPrice = 21m })
    .InsertEntryAsync(false);
batch += c =&gt; c
    .For&lt;Product&gt;()
    .Set(new { ProductName = "Test4", UnitPrice = 22m })
    .InsertEntryAsync(false);
await batch.ExecuteAsync();

Note that you don’t need to await batch operations as long as they don’t return any results: the operations are not executed instantly, they are recorded in a single batch message that is sent to the server in ExecuteAsync call.

But what if we need to return some data as a part of a batch? Here’s how it can be achieved:

IEnumerable&lt;Airline&gt; airlines1 = null;
IEnumerable&lt;Airline&gt; airlines2 = null;

var batch = new ODataBatch(client);
batch += async c =&gt; airlines1 = await c
    .For&lt;Airline&gt;()
    .FindEntriesAsync();
batch += c =&gt; c
    .For&lt;Airline&gt;()
    .Set(new Airline() { AirlineCode = "TT", Name = "Test Airline"})
    .InsertEntryAsync(false);
batch += async c =&gt; airlines2 = await c
    .For&lt;Airline&gt;()
    .FindEntriesAsync();
await batch.ExecuteAsync();

This code demonstrates how to implement scenario described in Advanced OData Tutorial at odata.org where the first batch operation retrieves a list of airlines, the second creates a new airline record and the third one refreshes the airline list (remember that it’s up to the server to return the updated or original data collection – OData batches are not database transactions, they don’t conform to ACID principles). Note also the use of async/await pattern in both FindEntriesAsync calls. I wrote earlier that you don’t need to await for operations being recorded but if you want to get hold of their responses you will either have to assign them to variables of type Task<T> or use async/await.

OData batch protocol also defines a way to reference newly created resources so they can be used to create associations with other resources. This scenario is also supported by Simple.OData.Client:

var product1 = new Product() {ProductName = "Test1", UnitPrice = 21m};
var product2 = new Product() {ProductName = "Test2", UnitPrice = 22m};

var batch = new ODataBatch(serviceUri);
batch += c =&gt; c
    .For&lt;Product&gt;()
    .Set(product1)
    .InsertEntryAsync(false);
batch += c =&gt; c
    .For&lt;Product&gt;()
    .Set(product2)
    .InsertEntryAsync(false);
batch += async c =&gt; await c
    .For&lt;Category&gt;()
    .Set(new { CategoryName = "Test3", Products = new[] { product1, product2 } })
    .InsertEntryAsync(false);
await batch.ExecuteAsync();

Here you see how Product entries “product1” and “product2” created in the batch are then referenced in the same batch from another new entry of a type Category that creates a link to a collection of associated products identified by the navigation property “Products”.

The revised batch API is available in Simple.OData.Client 4.0 RC6.

Announcing Simple.OData.Client 4.0: support for OData V4 and JSON payload

A few months ago I published a roadmap for Simple.OData.Client development which I was trying to follow. Today I am happy to announce a major revision (in fact major rewrite) of the library that now supports OData protocols from 1 to 4 and both Atom and JSON payload.

To achieve that I based the library parsers and formatters on Microsoft’s ODataLib that exists in two incarnations: for OData versions 1 to 3 (Microsoft.Data.OData) and for OData version 4 (Microsoft.OData.Core). Both ODataLib libraries implement similar sets of classes and interfaces (even with the same names), but they have different type identities which makes them incompatible. To make them transparent to the clients, Simple.OData.Client uses adapters libraries that communicate with respective ODataLib assemblies. These pictures show the libraries dependencies.

In case you use OData from desktop apps, the easiest is just to continue using Simple.OData.Client NuGet package that has everything to consume OData feeds of any OData version. However, mobile applications are usually more cautious about program footprint, and since in most cases an application connects to a specific version of OData service using specific version of OData protocol, there is no need to drag a couple of megabytes of unused dependencies, and in that case I recommend to install a NuGet package of Simple.OData.Client just for the given OData protocol version. Here are the packages:

  • Simple.OData.Client – supports all OData protocol versions, installs both sets of ODataLib components;
  • Simple.OData.V3.Client – supports versions of 1 to 3 of OData protocol, installs Microsoft.Data.OData package and dependencies;
  • Simple.OData.V4.Client – supports version 4 of OData protocol, installs Microsoft.OData.Core package and dependencies;

All packages support both Atom and JSON payload, but Atom payload is currently disabled in ODataLib V4 (should come later), so for the time being V4 clients can only use JSON.

Also note that Simple.OData.Client requires Microsoft.OData.Core 6.7.0 NuGet package and not the latest released 6.8.0. This is due to problems reading schemas with Enum types in the package 6.8.0 which I hope will be fixed soon.

Try it out, and if you find any issues please report them here.

Second look at Xamarin.Forms: building OData client

A few weeks ago I wrote a blog post about getting started with Xamarin.Forms. While I went through the full cycle of creating Xamarin.Forms applications on all three supported mobile platforms, the actual application (“Hello, Forms” greeting) was too dull to give an impression about the product UI capabilities.

After that I continued working on my pet project – a cross-platform OData client library Simple.OData.Client. In addition to a NuGet PCL package I also wanted to publish the library in Xamarin Component Store, and Xamarin Store submissions require samples for all supported platforms. Earlier I planned to write three separate samples, but with the release of Xamarin.Forms there is a much easier way to write small prototype-alike apps for such purposes. I asked folks at Xamarin whether it would be acceptable to submit component samples written using Xamarin.Forms, and it turned out that it would be the first such submission but it would be OK and in fact worth gathering some experience.

Prototyping NuGetFinder

The sample that I planned to write had to demonstrate capabilities of an OData library, and NuGet is definitely the most popular OData feed among developers. I decided to write a simple NuGet package finder with three pages: a page to specify package search criteria, a result page with a list of matching packages and a page with package details. I quickly made a UI prototype using Balsamiq Mockups.

Mockups

Choosing between Portable and Shared projects

As I wrote in my “First look” post, Xamarin Visual Studio add-on and Xamarin Studio install mobile apps templates that give developers a choice between applications based on Shared (Universal) projects and Portable projects. The first option is about sharing only projects assets, not resulting binaries. The second option is based on portable class libraries (PCL).

I am a big fan of portable class libraries, and Simple.OData.Client itself is a PCL, but after some considerations I decided to build Xamarin Store samples based on Shared projects. The main reason for this is that even though I wanted samples to reuse common code, I also wanted them to be autonomous, so a sample project assembly would contain its user interface rather that referencing another PCL.

C# or XAML?

Xamarin.Forms support describing user interface either using markup language (its own XAML dialect) or directly in C#. Both methods give same results, but for the time being none of them has comprehensive documentation, and with C# you are guarded by a compiler and guided by Intellisense. Writing XAML you must know markup and watch typos. I concluded that at this point it will be easier to describe UI elements in C#, but I plan to use XAML in the future when it’s better documented.

Where are those radio buttons?

I created a solution with shared UI assets and applications for iOS, Android and Windows Phone and started implementing its UI based on my mockups. I am a backend developer during daytime, so I have good excuses for not knowing much about available UI mobile controls. And it took me some time to realize that there is not such a thing as cross-platform radio button control. Xamarin doesn’t want to repeat a mistake made by designers of Java applets and offer cross-platform user interface elements that would look equally bad on each platform. Instead they offer UI abstractions that can translate into native controls. And since Xamarin.Forms is a multiplatform product, its UI elements are limited with those that can be found on all three platforms. Thus no radio or check buttons. I ended up with Picker control that provides the same functionality (although with a very different look).

Search page

Search page is the page that sets up and performs communication with the NuGet OData feed. The user enters search details and clicks “Search” button triggering the following code:

searchButton.Clicked += async (sender, e) =&gt;
{
    var packages = await FindPackagesAsync();
    var resultsPage = new ResultsPage(packages);
    await this.Navigation.PushAsync(resultsPage);
};

And here’s the definition of FindPackagesAsync:

private async Task&lt;IEnumerable&lt;Package&gt;&gt; FindPackagesAsync()
{
    int count = 0;
    switch (_resultSizePicker.SelectedIndex)
    {
        case 0: count = 10; break;
        case 1: count = 25; break;
        case 2: count = 100; break;
    }

    var odataClient = new ODataClient("https://nuget.org/api/v1");
    var command = odataClient
        .For&lt;Package&gt;("Packages")
        .OrderByDescending(x =&gt; x.DownloadCount)
        .Top(count);

    switch (_sortPicker.SelectedIndex)
    {
        case 0:
            command.OrderByDescending(x =&gt; x.DownloadCount);
            break;
        case 1:
            command.OrderBy(x =&gt; x.Id);
            break;
        case 2:
            command.OrderByDescending(x =&gt; x.LastUpdated);
            break;
    }

    if (!string.IsNullOrEmpty(_searchText.Text))
    {
        command.Filter(x =&gt; x.Title.Contains(_searchText.Text) &amp;&amp; x.IsLatestVersion);
    }
    else
    {
        command.Filter(x =&gt; x.IsLatestVersion);
    }

    command.Select(x =&gt; new {
        x.Id, x.Title, x.Version, x.LastUpdated, x.DownloadCount, 
        x.VersionDownloadCount, x.PackageSize, x.Authors, x.Dependencies });

    return await command.FindEntriesAsync();
}

Here how the Search page looks on iPhone simulator:

SearchPage

Nothing fancy, but enough to prototype page UI on all major mobile platforms.

Results page

Results page is essentially a list of packages satisfying search criteria with an event handler for a selected item:

resultList.ItemSelected += (sender, e) =&gt;
{
    var package = (PackageViewModel)e.SelectedItem;
    var detailsPage = new DetailsPage();
    detailsPage.BindingContext = package;
    Navigation.PushAsync(detailsPage);
};

Here’s how the results pages look on an Android simulator:

ResultsPage

Details page

Selected package details are shown on a new page that consists of label and text box controls. The programming logic is very simple, here’s a code extract that assigns package title and package id:

var titleLabel = new Label() { Font = Font.SystemFontOfSize(NamedSize.Large) };
titleLabel.SetBinding(Label.TextProperty, "Title");
stackLayout.Children.Add(titleLabel);
var idLayout = new StackLayout() { Orientation = StackOrientation.Horizontal };
idLayout.Children.Add(new Label() { Text = "Package Id: " });
var idLabel = new Label();
idLabel.SetBinding(Label.TextProperty, "Id");
idLayout.Children.Add(idLabel);
stackLayout.Children.Add(idLayout);

Here’s how Details page look on Windows Phone emulator:

DetailsPage

And now there is a new Xamarin component!

These samples have been packaged and submitted together with Simple.OData.Client library. They are now available in Xamarin Component Store. And the sample source code is also available from the GitHub repository.

Component

Simple.OData.Client roadmap: versions 2, 3 and 4

It’s been a few month since I wrote a blog post comparing features of Simple.OData.Client and WCF Data Services client. Judging from NuGet download statistics, questions and GitHub issues I can see that the library has been used in various scenarios and on various platforms, including Xamarin iOS and Android. This is indeed very encouraging but at the same time raises the bar for the platform tests, conformance to OData protocol and API expressiveness. Some of the forthcoming changes to Simple.OData.Client API are breaking, and with respect to semantic versioning I have to release them with a different major version number. So I’d like to give in this blog post a quick overview of each version.

Version 1

Initial release of Simple.OData.Client came as a library extracted from Simple.Data OData adapter to improve its granularity and portability. The library implemented HTTP communication based on HttpWebRequest and favored synchronous calls. This didn’t work well with platforms like Silverlight and Windows Phone that required asynchronous communication all the way.

Version 2

This is the most recent (in May 2014) version of Simple.OData.Client with two major changes comparing to version 1:

  • The library is now using Microsoft HttpClient PCL (Microsoft.Net.Http);
  • The library API has been redesigned to be asynchronous but it still exposes obsolete synchronous methods in addition to asynchronous ones;

I have to admit now that keeping synchronous methods was a mistake – I did it for backward compatibility, but it misleads developers in belief that they can use synchronous API on platforms where it can easily cause deadlocks. In fact, I wouldn’t recommend to use sync API on any platform except .NET 4.x. For the sake of existing .NET 4 clients I will keep adding minor changes to version 2 even after release of newer major versions of the library.

Version 3

This is already available as release candidate and contains the following significant changes:

PCL profile numbers may not say much, but this is in fact the major change although not reflected in API method signatures. PCL profile 147 corresponds to so called legacy surface area intended to fit most platforms. This requires use of internal wrappers and adapters and as it turned out results in problems – ironically on the legacy platforms. For example, I never managed to succeed using HttpClient PCL from another PCL called from a Silverlight application. So I changed the PCL targeting strategy and now Simple.OData.Client targets .NET 4.0 and Silverlight platforms via native platform-specific libraries with the its PCL targeting other platforms.

Version 4

Currently Simple.OData.Client uses its own parser and formatter that only support XML payload in the format of RSS feeds. That was the original OData message format, but the protocol has been extended with support for JSON, and being more compact, more readable and more network bandwidth friendly, JSON is becoming a preferred OData payload representation and is selected as a default format by OData feeds built using HTTP Web API. Now that Microsoft has converted its ODataLib (Microsoft.Data.OData) to support all target Simple.OData.Client platforms, I plan to retire internal Simple.OData.Client OData parser and formatter and instead use the one from ODataLib so I can ensure the same message serialization and keep the library up-to-date with most recent OData protocol updates reflected in ODataLib.

Summary: which version to use?

 

  • If you’re using Simple.Data OData adapter, you don’t need to care: just use the most recent version available at NuGet or keep using the one you’re using now if it works fine. Simple.Data OData adapter will be maintained and its dependencies will be updated so you don’t have to take any actions in regards to Simple.OData.Client.
  • If you’re using Simple.OData.Client synchronous API with .NET 4.0 applications and everything works fine, you can continue using it. For mobile applications use only asynchronous API calls.
  • The release of the version 3 is around the corner, pre-release is already available. Works much better with Silverlight clients, support for cancellation tokens, modern PCL profile widely used on Xamarin platforms.
  • Version 4 will add support for JSON payload and will be based on ODataLib. But I haven’t even started working on it.

12 reasons to consume OData feeds using Simple.OData.Client

In his great blog post about NuGet dependency monitor Mauricio Scheffer devoted a whole section to WCF Data Services criticism. And it’s well deserved criticism. Anyone who used WCF Data Services client for anything more than a simple demo knows what Mauricio is talking about. It’s been about three years since I was exposed to WCF Data Services, and I remember how pleased I was at first to discover that I could send LINQ expressions to WCF Data Services client that was converting them to OData HTTP requests. But I also remember my disappointment over later discoveries: I couldn’t write LINQ expressions the way I was used to when working with SQL databases. I had to carefully think about the LINQ expression syntax, and test, test, test. They all compiled but I never knew for sure if they are not going to explode at runtime, either on a client or a server side.

Read last section of Mauricio Scheffer’s blog post, and you will understand how fragile WCF Data Services LINQ expressions are. If you want to project query result to a single column, you can’t just write “x.Id” – you need to write “new { x.Id }”. Negate a boolean expression – and you will receive a cryptic error message. Swap Where and Select clauses – and previously working statement no longer works. And all these failures have nothing to do with OData protocol restrictions (such as no support for JOINs). They are all limitations of WCF Data Services LINQ expression parser – mostly on a client side, sometimes on a server side.

My own dissatisfaction with WCF Data Services LINQ support was so big that I even wrote my own OData client. It was originally packaged as a Simple.Data OData adapter, adding to a collection of adapters for an excellent micro-ORM developed by Mark Rendle. However, Simple.Data API is modeled around relational databases thus leaving out some less common OData scenarios, in addition I wanted my client to support Mono and non-Windows mobile platforms. So I extracted from Simple.Data OData adapter a platform independent core and packaged it as Simple.OData.Client (internally used by Simple.Data OData adapter). And of course when I read Mauricio’s post, I was immediately provoked to test on Simple.OData.Client commands that WCF Data Services client failed to execute.

Since you are reading this blog post, you may guess that I was pleased with results. Not only Simple.OData.Client supports features not implemented in WCF Data Services, it offers syntax alternatives, and I will further present variations of its fluent interface: typed and dynamic. If you’re already using Simple.Data, you may stick to more familiar Simple.Data OData adapter, although Simple.OData.Client supports more advanced OData protocol features and is packaged as portable class library that can be used on non-Windows platforms, such as iOS and Android. It also gives a choice of either typed or dynamic API (so it effectively offers multiple API flavors). I will show examples using both typed and dynamic Simple.OData.Client syntax, but every example will be originated with its WCF Data Services counterpart. And you can judge yourself which API works better.

Libraries used in comparison

WCF Data Services client

Visual Studio has built-in support for generating OData client access code: simply choose “Add Service Reference…” from project context menu and enter a URL of the existing OData service. You will get proxy code that contains entities (OData service resources), and you will be able to manipulate them using Entity Framework-alike API. Well, as long as you manage to get LINQ statements right. In order to get most recent WCF Data Services client components, install them from NuGet.

Simple.OData.Client

Simple.OData.Client (also available from NuGet) is a portable class library that once has been extracted from Simple.Data OData adapter. It has its own Wiki pages at GitHub, but I believe the easiest way to understand its features is to check its tests. As I mentioned earlier, it can target various platforms including .NET 4.x, Windows Store, Silverlight 5, Windows Phone 8, iOS and Android. If you need to consume OData feeds consisting of interlinked resource sets I believe it provides the simplest yet powerful API with typed and dynamic flavors.

Retrieving data

In .NET world data sources are expected to have LINQ providers, and sometimes LINQ providers are written even for databases that don’t expose its data in tabular or relational form. So it was quite logical for Microsoft developers to offer a LINQ provider for clients of its OData protocol, and this provder has grown from a project “Astoria” to become WCF Data Services. They deliberately made client interface look like to LINQ to Entities, so a simple LINQ statement is indistinguishable from a LINQ to Entities call:

var product = ctx.Products.Where(x => x.ProductName == “Chai”).Single();

I am using here fluent LINQ syntax, but of course all these statements can be written using query comprehension. The variable “ctx” represents the data context that is responsbile for translating commands to HTTP requests and sending them to an OData service.

As I already mentioned, Simple.OData.Client offers several syntax flavors, we will focus on two of them: a typed and a dynamic ones. A typed request to an OData service will be like like this:

var product = client.For<Products>().Filter(x => x.ProductName == “Chai”).FindEntry();

And here’s a dynamic one:

var x = ODataDynamic.Expression;
var product = client.For(x.Products).Filter(x.ProductName == “Chai”).FindEntry();

Note that when using dynamic API you need first to define an instance of a dynamic expression object (“x”) that can be used for the rest of the code block (even in multiple calls) in forms of various expressions that are evaluated at runtime and converted to parts of OData commands.

Now that we have a basic idea of how to query OData services using different OData client libraries, let me guide you through some specific examples and show why WCF Data Services client may lose its attractiveness.

Reason 1: projection to a single column

WCF Data Services

This is a widely used scenario, therefore it comes as a complete surprise that it’s not supported by WCF Data Services. Here’s an example:

var productIDs = ctx.Products.Select(x => x.ProductID);

If you run it it will fail with the following error:

System.NotSupportedException : Individual properties can only be selected from a single resource or as part of a type. Specify a key predicate to restrict the entity set to a single instance or project the property into a named or anonymous type.

“As part of a type”? Let’s select it as a part of an anonymous type. Then it will work:

var productIDs = ctx.Products.Select(x => new { x.ProductID });

Like Mauricio Scheffer, I don’t see any logic in this restriction. LINQ supports single column projections, WCF Data Services server side supports single column projections. This is a pure client side LINQ expression handler limitation which should have been fixed long ago.

Simple.OData.Client (typed)

Here’s the code using that uses typed entities:

var productIDs = client.For<Products>().Select(x => x.ProductID).FindEntries(); 

Simple.OData.Client (dynamic)

Finally dynamic version of the code:

IEnumerable<dynamic> productIDs = client.For(x.Products).Select(x.ProductID).FindEntries();

Note that we can use “Products” instead of “Product”. Like Simple.Data, Simple.OData.Client has a simple built-in word matcher that accepts both singular, multiple and underscore-separated names and tries to find the matching metadata element.

Reason 2: reduntant query conditions

This is perhaps not a common scenario, however it may occur when LINQ expression is built in a loop, and it’s a perfectly valid LINQ expression. It just contains redundant clause.

WCF Data Services

This code will fail:

var product = ctx.Products.Where(x => x.ProductID == 1 && x.ProductID == 1).Single();
System.NotSupportedException : Multiple key predicates cannot be specified for the same entity set.

Sure you can’t look up an entity using multiple keys. But if the second key clause is identical to the first one, why not simple optimize the expression and remove redundancy? Simple.OData.Client performs it fine.

Simple.OData.Client (typed)

var product = client.For<Product>()
    .Where(x => x.ProductID == 1 && x.ProductID == 1).FindEntry();

Simple.OData.Client (dynamic)

var product = client.For(x.Products)
    .Select(x.ProductID == 1 && x.ProductID == 1).FindEntry();

Reason 3: deceiving LINQ extension methods

WCF Data Services

Let’s have a look at the statement similar to the one used in the previous example:

var product = ctx.Products.Where(x => x.ProductID == 1).Single();

If you have ReSharper installed on your machine, you should a green wavy line under that statement. Hover the mouse, and ReSharper will display a tooltip suggesting replacing Where and Single clauses with just one call:

LinqWarning

This is a natural code simplification supported by most LINQ providers. Unfortunately LINQ to OData provider doesn’t support such syntax optimization, and if you press Alt+Enter to accept the suggestion and replace the code above with the command

var product = ctx.Products.Single(x => x.ProductID == 1);

… you will get the following error:

System.NotSupportedException : The method 'Single' is not supported.

Too bad. Single, First, Last, SingleOrDefault etc. – all these LINQ extension methods must be applied to the result collection after the query execution completes, you can not send them as a part of a server request. To make the matter worse, you won’t get any warning if you use one of these statements as a server request – quite opposite, as we just saw, you will get a ReSharper notice if you don’t use them. The error will happen later, at runtime.

Simple.OData.Client use different methods to retrieve single and multiple results to ensure server-side result optimization:

Simple.OData.Client (typed)

// Returns first result 
var products = client.For<Product>().Where(x => x.ProductID == 1).FindEntry(); 
// Returns all results
var products = client.For<Product>().Where(x => x.ProductID == 1).FindEntries();

Simple.OData.Client (dynamic)

// Returns all results
var product = client.For(x.Products).Where(x.ProductID == 1).FindEntries();
// Returns all results
var product = client.For(x.Products).Where(x.ProductID == 1).FindEntry(); 

When retrieving multiple results, you can of course append Single/First/Last LINQ extension methods to the statements as you would do when using LINQ provider. This statements will be applied to the result collection after it has been retrieved from the server.

Reason 4: swapping command clauses

There should be no reason why you couldn’t swap Where and Select clauses? Let’s try.

WCF Data Services

var productID = ctx.Products
    .Where(x => x.ProductID != 1).Select(x => new { ProductID }).First();

The statement above works fine (note that we had to use anonymous type in the Select clause, otherwise it would have failed). Now we’ll apply Where to the projection:

var productID = ctx.Products
    .Select(x => new { ProductID }).Where(x => x.ProductID != 1).First();

ReSharper immediately comes with the suggestion to merge Where and First into a single clause, but we know it’s going to fail. Unfortunately the whole statement is going to fail anyway:

System.NotSupportedException : The filter query option cannot be specified after the select query option.

Simple.OData.Client doesn’t have this problem:

Simple.OData.Client (typed)

var productID = client.For<Product>()
    .Where(x => x.ProductID != 1).Select(x => x.ProductID).FindEntry();
var productID = client.For<Product
    .Select(x => x.ProductID)>().Where(x => x.ProductID != 1).FindEntry();

Simple.OData.Client (dynamic)

var productID = client.For(x.Products)
    .Where(x.ProductID != 1).Select(x.ProductID).FindEntry();
var productID = client.For(x.Products)
    .Select(x.ProductID)).Where(x.ProductID != 1).FindEntry(); 

Reason 5. Expanding related entities

Although OData protocol does not support joins, it has a notion of related entities that can be navigated and expanded. So if Products entities has columns CategoryID referring to Categories entities and there is a Products.Category association defined in service metadata, it should be possible to navigate to Categories from Products via Category association.

WCF Data Services

WCF Data Services LINQ provider has a custom method Expand that can be used for eager loading of related entities. So if we want to expand products with associated categories we might use the following statement:

var products = ctx.Products.Where(x => x.ProductName == “Chai”).Expand(x => x.Category);

Nope! It doesn’t even compile. And the reasons for this is custom Expand method applied after Where clause. “Where” is a standard extension method that returns IQueryable, but Expand is defined on DataServiceQuery class that is a part of WCF Data Services, so it can only be applied to generated proxy collection classes (“Products”, “Categories” etc.), and can’t be interchanged with standard LINQ extension methods.

Again, we are facing a problem with swapped LINQ command clauses, now due to use of custom method. Here’s the statement that works:

var products = ctx.Products.Expand(x => x.Category).Where(x => x.ProductName == “Chai”);

But what if we want to add a projection clause to this statement and only select the category? Let’s try:

var categories = ctx.Products.Expand(x => x.Category).Where(x => x.ProductName == “Chai”)
    .Select(x => x.Category);

Hmm, an error:

System.NotSupportedException : Can only specify query options (orderby, where, take, skip) after last navigation.

Maybe this is because we didn’t use anonymous type in the Select clause? Alright, another attempt:

var categories = ctx.Products.Expand(x => x.Category).Where(x => x.ProductName == “Chai”)
    .Select(x => new { x.Category });

Oops, this time the error message is different:

System.NotSupportedException : Cannot create projection while there is an explicit expansion specified on the same query.

But at least it gives us some clue. It’s not permitted to combine Expand and Select clauses. Out of my understanding, but let’s drop Expand then:

var categories = ctx.Products.Where(x => x.ProductName == “Chai”)
    .Select(x => new { x.Category });

It worked now, but you just saw how fragile the WCF Data Services LINQ provider. You just have to keep on trying different syntax variations until you find something that won’t fail at runtime.

For eager loading of relations Simple.OData.Client defines a method Expand that we can freely swap with other methods of its fluent interface, including filtering and projection:

Simple.OData.Client (typed)

var product = client.For<Products>()
    .Expand(x => x.Category).Filter(x => x.ProductName == "Chai").FindEntry();

var product = client.For<Products>().Filter(x => x.ProductName == "Chai")
    .Expand(x => x.Category).FindEntry();

var product = client.For<Products>().Expand(x => x.Category).Filter(x => x.ProductName == "Chai")
    .Select(x => new { x.ProductID, x.Category.CategoryID }).FindEntry();

Simple.OData.Client (dynamic)

var product = client.For(x.Products)
    .Expand(x.Category).Filter(x.ProductName == "Chai").FindEntry();

var product = client.For(x.Products)
    .Filter(x.ProductName == "Chai").Expand(x.Category).FindEntry();

var product = client.For(x.Products).Expand(x.Category).Filter(x.ProductName == "Chai")
    .Select(x.ProductID, x.Category.CategoryID).FindEntry();

Reason 6: navigating to a related entity

In addition to expanding relations OData protocol supports navigation to related entities, so it should be possible to bypass the parent entry and go directly to its relations. There is a constraint on search conditions: the parent entry should be looked up by its key, so the navigation is performed from a single entry.

WCF Data Services

WCF Data Services doesn’t support navigation in a strict sense, but as we saw in the previous section, it’s possible to simulate navigation using projection on a property that represents the relation:

var categories = ctx.Products.Where(x => x.ProductID == 1)
    .Select(x => new { x.Category });

This is not a real navigation to a related entity for two reasons. First, the statement returns a collection of anonymous types, not category objects, so we need to access the actual Category object through a Category property of the anonymous type. Second, the statement generates a different OData command: filter instead of a key lookup. So in principle you can retrieve categories for multiple products, for example by issuing the following command:

var categories = ctx.Products.Where(x => x.ProductName != 1)
    .Select(x => new { x.Category });

Unlike WCF Data Services client, Simple.OData.Client has a proper support for navigation using method NavigateTo:

Simple.OData.Client (typed)

var category = client.For<Product>()
    .Where(x => x.ProductName == “Chai”).NavigateTo<Category>().FindEntry();

Simple.OData.Client (dynamic)

var category = client.For(x.Product)
    .Where(x.ProductName == “Chai”).NavigateTo(x.Category).FindEntry();

Reason 7: chained navigation

WCF Data Services

Due to lack of proper navigation support WCF Data Services client can’t be used to fetch results of multi-level navigation. You can only retrieve the information that belongs to an immediate descendant, an attempt to reach deeper levels won’t work:

var product = ctx.Products
    .Where(x => x.ProductID == 1).Select(x => new { x.Category, x.Category.Products })

The above statement will succeed, but product.Category.Products will contain no elements. For the same reason the following statements will both fail with “sequence contains no elements” exception:

var employee = ctx.Employees.Where(x => x.EmployeeID == 14)
    .Select(x => new { x.Superior })
    .Select(x => new { x.Superior.Superior }).First();

var employees = ctx.Employees.Where(x => x.EmployeeID == 14)
    .Select(x => new { x.Superior })
    .Select(x => new { x.Superior.Subordinates });

Simple.OData.Client is another story: it fully supports navigation, so all the above scenarios can be easily expressed using both typed and dynamic syntax:

Simple.OData.Client (typed)

var products = client.For<Products>().Filter(x => x.ProductID == 1)
    .NavigateTo(x => x.Category)
    .NavigateTo(x => x.Products)
    .FindEntries();

We can perform deeper navigation, navigating from an employee to a superior of superior:

var employee = client.For<Employees>().Filter(x => x.EmployeeID == 14)
    .NavigateTo(x => x.Superior)
    .NavigateTo(x => x.Superior)
    .FindEntry();

Or we can fetch multiple results if we request subordinates of the superior:

var employees = client.For<Employees>().Filter(x => x.EmployeeID == 14)
    .NavigateTo(x => x.Superior)
    .NavigateTo(x => x.Subordinates)
    .FindEntries();

Simple.OData.Client (dynamic) Here are similar statements for dynamic API:

var products = client.For(x.Products).Filter(x.ProductID == 1)
    .NavigateTo(x.Category)
    .NavigateTo(x.Products)
    .FindEntries();
var employee = client.For(x.Employees).Filter(x.EmployeeID == 14)
    .NavigateTo(x.Superior)
    .NavigateTo(x.Superior)
    .FindEntry();
var employees = client.For(x.Employees).Filter(x.EmployeeID == 14)
    .NavigateTo(x.Superior)
    .NavigateTo(x.Subordinates)
    .FindEntries();

Reason 8: selecting a derived type entity

Let’s have a brief look at rather advanced (and rarely used in OData services) scenario: subclassing OData resource types. Personally I don’t fancy bringing OOP to RESTful (or REST-alike) Web services, but since OData protocol has support for base and derived resource types, it’s fair to examine how our libraries deal with it.

So far I have been using classical Northwind model, but it doesn’t have derived classes. So I extended it with a Transport and Ship entity type: Ship subclasses Transport and add ShipName property. TransportType property on a base class is used as a type discriminator.

WCF Data Services

Selecting a Transport instance without regards to its type is straightforward:

var transport = ctx.Transport.First();

Next is to select only ships:

var ship = ctx.Transport.Where(x => x is Ships).First();

To gain access to ShipName property we need to cast the result:

var shipName = ctx.Transport.Where(x => x is Ships).First() as Ships;

What if we want to search by ship name? This is also possible:

var ship = ctx.Transport
    .Where(x => x is Ships && (x as Ships).ShipName == “Titanic”).First();

From the last example you may imagine that you can cast the results to the derived type using Select clause, so you will get back properly typed results:

var ships = ctx.Transport.Where(x => x is Ships).Select(x => x as Ships);

But this won’t work:

System.NotSupportedException : Unsupported expression '(x As Ships)' in 'Select' method. Expression cannot end with TypeAs.

You always obtain results as a base type and will need to cast them to a derived type on a client side. However, comparing to problems we experienced with other queries, derived type management in WCF Data Services works almost as expected. But let’s check how it is implemented in Simple.OData.Client, and I believe it will become clear which syntax is more intuitive.

Simple.OData.Client (typed)

Simple.OData.Client has a generic method As<T> that redefines the type of entities being processed. Once the cast is performed, all subsequent calls on the fluent chain will be made using that type:

var transports = client.For<Transport>().FindEntries();
var ships = client.For<Transport>().As<Ships>().FindEntries();
var ship = client.For<Transport>().As<Ships>().Filter(x => x.ShipName == "Titanic").FindEntry();

Simple.OData.Client (dynamic)

In a similar manner, dynamic Simple.OData.Client API has a method As, it’s just that this method is not generic and the derived entity type is evaluated based on the dynamic expression parameter:

var transports = client.For(x.Transport).FindEntries();
var ships = client.For(x.Transport).As(x.Ships).FindEntries();
var ship = client.For(x.Transport).As(x.Ships).Filter(x.ShipName == "Titanic").FindEntry();

Modifying data

WCF Data Services client use same methods as Entity Framework to add, update and delete OData resources, it also has a notion of data context, so all operations are queued until the call to SaveChanges and only then are sent to an OData service. Simple.OData.Client doesn’t have a concept of data context (although multiple operations can be packed in batches according to OData protocol), but eventually data modification methods provided by both libraries are mapped to equivalent HTTP requests.

Here’s an example of creating an entry using WCF Data Services client:

var product = new Products { ProductName = "Milk" };
ctx.AddToProducts(product); 
ctx.SaveChanges();

And here’s Simple.OData.Client counterparts:

var product = client.For<Products>().Set(new { ProductName = “Milk” }).InsertEntry();

or

var product = client.For<Products>().Set(new Products { ProductName = “Milk” }).InsertEntry();

or

var product = client.For(x.Products).Set(x.ProductName = “Milk”).InsertEntry();

The important difference is that in WCF Data Services API a central role is given to the context. It’s a container, a stack of operations and entities. Whatever you intend to do must go via the context, it needs to track all entities prior them to be sent to the server, and it’s developer’s responsibility to ensure the entities are tracked. This is what makes it quite different from Entity Framework that is capable of auto-tracking the affected entities. As long as you are dealing with isolated entities, it shouldn’t be inconvenient. But relationship management has some pitfalls. Let’s have a look at some of them and compare with how this can be done using Simple.OData.Client.

Reason 9: Assigning a linked entity

WCF Data Services

The following code looks reasonable except that it doesn’t work:

var category = ctx.Categories.Where(x => x.CategoryName == "Beverages").Single();
var product = new Products { ProductName = “Chai”, Category = category };
ctx.AddToProducts(product);
ctx.SaveChanges(); 

The code doesn’t fail and the product is created, but it is not linked to a category. Unlike Entity Framework that detects assignments of association properties, the context (“ctx”) needs to be explicitly notified about the intention to link the product to the category. So we need to modify the code, and if you search for available options you will find several choices: AddLink, AddRelatedObject and SetLink. Which one is the right in every particular scenario depends on relation cardinality. In the example above SetLink is the right one, so the code that works looks like this:

var category = ctx.Categories.Where(x => x.CategoryName == "Beverages").Single();
var product = new Products { ProductName = “Chai”, Category = category };
ctx.AddToProducts(product);
ctx.SetLink(product, "Category", category);
ctx.SaveChanges();

There are several reasons why I dislike such approach:

  • A library could have been smarter to figure out that the object has an association property assigned and take the burden of additional linking code from developers;
  • The service metadata has all information about relation cardinality, so there is no need to force developers to be explicit about what kind of relation they are establishing;
  • Use of magic strings sucks. WCF Data Services uses lambda expressions in other methods, it should have allowed lambdas here too.

Now let’s see how linking is managed by Simple.OData.Client

Simple.OData.Client (typed)

var category = client.For<Categories>().Filter(x => x.CategoryName == "Beverages").FindEntry();
var product = client.For<Products>()
    .Set(new { ProductName = "Chai", Category = category }).InsertEntry();

Couldn’t be easier, could it? And here’s a dynamic version:

Simple.OData.Client (dynamic)

var category = client.For(x.Categories).Filter(x.CategoryName == "Beverages").FindEntry();
var product = client.(x.Products)
    .Set(new { ProductName = "Chai", Category = category }).InsertEntry(); 

Reason 10: Detaching a linked entity

WCF Data Services

I guess you are already prepared that detaching a link won’t be easy, and I can assure you it won’t be. In fact it was even more difficult than I thought. No need to say that the following code won’t work:

var product = ctx.Products.Where(x => x.ProductName == "Chai").Single();
product.Category = null;
ctx.UpdateObject(product);
ctx.SaveChanges();

The code doesn’t fail, it just doesn’t affect anything. Again, due to the unaware (or should I say ignorant) context. But having learned about SetLink method in the previous section we might try that:

var product = ctx.Products.Where(x => x.ProductName == "Chai").Single();
ctx.SetLink(product, "Category", null);
ctx.UpdateObject(product);
ctx.SaveChanges();

Nope. Doesn’t work either. The WCF Data Services context doesn’t treat a null association object as an intention to detach the link. But there is a DetachLink method, let’s try that instead:

ctx.DetachLink(product, "Category", category);

No, the relationship is still there. Perhaps DeleteLink?

ctx.DeleteLink(product, "Category", category);

This time the code fails at runtime:

System.InvalidOperationException : AddLink and DeleteLink methods only work when the sourceProperty is a collection.

Let’s change it then so the link is deleted from a category:

ctx.DeleteLink(product, "Category", category);

No, if I reload the product it still shows the linked category. But apparently this is due to the internal cache in the context object. If I clear the context and reload the product the link will disappear. So after some frustrations here is the code that works:

var category = ctx.Categories.Where(x => x.CategoryName == "Beverages").Single();
var product = ctx.Products.Where(x => x.ProductName == "Chai").Single();
ctx.DeleteLink(category, "Products", product);
ctx.UpdateObject(product);
ctx.SaveChanges();
// Without this call if you reload the project it will still show a linked category!
ctx.DetachProduct();

Well, you can judge yourself how intuitive the above code is. And now how the same thing can be achieved using Simple.OData.Client.

Simple.OData.Client (typed)

var product = client.For<Products>().Filter(x => x.ProductName == "Chai").FindEntry();
var category = client.For<Categories>().Filter(x => x.CategoryName == "Beverages").FindEntry();
client.For<Products>().Key(product.ProductID).UnlinkEntry(x => x.Category);

Note use of Key clause. I could use Filter clause instead as long as the filter condition represented a key lookup.

Simple.OData.Client (dynamic)

var product = client.For(x.Products).Filter(x.ProductName == "Chai").FindEntry();
var category = client.For(x.Categories).Filter(x.CategoryName == "Beverages").FindEntry();
client.For(x.Products).Key(product.ProductID).UnlinkEntry(x.Category);

An alternative to using UnlinkEntry method is to set the Category property to null and call UpdateEntry.

Reason 11: Modifying multiple entries

Neither Entity Framework nor WCF Data Services client has built-in support for update of multiple entries based on search condition. OData protocol doesn’t offer this feature either – you have to fetch query results (or at least its key values) and then go through them and update one after another. But this is not uncommon scenario when you need to change or delete all entities matching certain criteria, so Simple.OData.Client offers this functionality.

WCF Data Services

As I said, WCF Data Services client doesn’t expose a method to modify or delete multiple results of a query execution in a single class, so the you will have to do something like this:

foreach (var product in ctx.Products.Where(x => x.ProductName.StartsWith(“A”))
{
    ctx.DeleteObject(product);
}
ctx.SaveChanges();

Simple.OData.Client offers methods UpdateEntries and DeleteEntries that takes care of querying the OData service and traversing the result set. Below are the examples.

Simple.OData.Client (typed)

client.For<Products>().Filter(x => x.ProductName.StartsWith(“A”)).DeleteEntries();

Simple.OData.Client (dynamic)

client.For(x.Products).Filter(x.ProductName.StartsWith(“A”)).DeleteEntries();

Final thoughts

Reason 12: REST is not SOAP

We have gone through several examples that demonstrate obstacles of reading and updating OData feeds with WCF Data Services clients. However there is one more, on a conceptual level. Microsoft has chosen to present OData services to Visual Studio users as if it were old fashioned SOAP services described with its WSDL files. I believe that majority of developers who are first introduced to OData services when they choose “Add Service Reference” from a project menu don’t even realize that they are exposed to REST communication. From within Visual Studio it all looks like a traditional WCF service with a contract and data types. Sure, OData resource types may be looked upon as classes with DataContract attribute, and HTTP verbs GET, POST, PUT/MERGE and DELETE are similar to WCF service operations. But analogy with WCF, SOAP and event Entity Framework is misleading. I’d rather see better exposure of HTTP verbs, so developers have a clear picture of what operations are safe, idempotent or neither of them.

While Simple.OData.Client API doesn’t include methods named after HTTP verbs, it has methods with straightforward mapping to them. FindEntries and FindEntry correspond to GET, they retrieve either all or first matching result. InsertEntry, UpdateEntry and DeleteEntry are mapped to POST, PUT/MERGE and DELETE respectively. LinkEntry and UnlinkEntry provide higher abstraction over calls HTTP verbs to establish or remove relationships between entities. These methods (all named <Verb>Entry or <Verb>Entries) are always final clauses in the chain of Simple.OData.Client fluent interface calls. All but last elements in the call chain incrementally build an OData request, and the final call to <Verb>Entry invokes the corresponding HTTP command. I believe that such API gives a better understanding of underlying HTTP communication and help developers make efficient use of OData protocol.