OData Services

A Breeze client can consume any standard OData feed “as is” when you configure the client for OData.

Topics elsewhere cover configuring for OData BreezeJS Clients and for OData Breeze Sharp clients.

OData is a cross-platform standard (with several versions). Unfortunately, not every OData service fully complies with the standard. The deviations from the metadata specifications can easily trip you up.

If you have no control over the server, you may have to abandon the server's $metadata feed and construct the MetadataStore on the client ... as described in the BreezeJS documentation and the Breeze Sharp Documentation.

If you do control the server you may be able to make adjustments on the server that improve the situation for the Breeze client. In this topic we explore your options on some well known OData server stacks.

WCF Data Services (WCF OData)

This is Microsoft's original OData implementation. It is the most compliant implementation and Breeze clients should have no trouble with the metadata it generates.

However, Microsoft seem to have redirected their energies toward development of ASP.NET Web API OData and away from WCF Data Services. The future of WCF Data Services is anyone's guess.

ASP.NET Web API OData Service

Web API OData is both the present and future of OData services from Microsoft.

It is rapidly evolving and is highly compliant with the latest OData standards while offering a number of superb manageability, security and customization capabilities.

We highly recommend Brian Noyes' brilliant blog post with step-by-step instructions for building a Breeze/Web API OData sample which includes a downloadable sample. We also recommend his Pluralsight course "Building ASP.NET Web API OData module, "Consuming OData Services..." module that demonstrates consuming a Web API OData service with a Breeze client.

You must consider certain peculiarities of the Web API OData that affect your design.

The Breeze Web API OData JavaScript sample attempts to mitigate some of the more unfortunate consequences of these "peculiarities".

No transactional save

A "$batch" save to a WCF OData service backed by an Entity Framework model is a transactional save. That means every entity in the change-set either saves successfully or none of them do.

A "$batch" save to a Web API OData service is non-transactional out-of-the-box no matter what mechanism you use for a backing store. That means some entity operations in the batch could be saved even if others are rejected. You have to deal with the consequences.

For the record, this is a violation of the OData $batch spec:

All operations in a ChangeSet represent a single change unit so the server must successfully process and apply all the requests in the ChangeSet or else apply none of the requests in the ChangeSet. It is up to the service to define rollback semantics to undo any requests within a ChangeSet that may have been applied before another request in that same ChangeSet failed and thereby honor this all-or-nothing requirement.

For what it's worth, the BreezeJS WebAPIOData adapter returns information that reveals which entity changes were saved and which failed.

Save order is not guaranteed

When you "$batch" save to a WCF OData service backed by an Entity Framework model, EF takes care of ordering the operations properly. For example, if you create a new Order and a new OrderDetail, EF ensures that the parent Order is inserted first before its child OrderDetail.

In a "$batch" save to a Web API OData service the order of save is not guaranteed and there is nothing you can do on the client to ensure that the service will save the entity changes in the correct order.

Even if your batch specifies the new Order before the new OrderDetail in separate ChangeSets, the Web API service might try to save the OrderDetail first. This could (and probably will) fail the database referential integrity check, causing the OrderDetail save to fail.

For the record, this is a violation of the OData $batch spec:

The order of ChangeSets and retrieve requests within a Batch request is significant as it states the order in which the service processes the components of the Batch.

It is up to you to ensure that no $batch save has the potential to trigger a referential integrity violation, most likely by chaining separate saveChanges() for changes that must be processed in a particular order.

Metadata fail!

Microsoft's instructions for creating a Web API OData controller tell you to describe your API data model with the ODataConventionModelBuilder. This component is responsible for generating metadata from the model.

Unfortunately, it does NOT produce fully compliant metadata. Most egregiously, it omits foreign key information a Breeze client needs to maintain navigation properties. It also doesn't handle namespaces properly.

Microsoft has promised to repair the deficiencies but has not done so yet and has no timetable for doing so. After almost a year of waiting we have grown pessimistic.

You won't have relationship support on the Breeze client if you use ODataConventionModelBuilder to describe your model. You won't be able to use "$expand" to include related entities in the query results. You won't be able to navigate among related entities. Breeze won't maintain the navigation properties.

Why? Because the metadata it generates don't identify the foreign key properties Breeze needs to wire up the relationships.

As yet there is no officially endorsed way to repair the navigation metadata on the client. We cannot stop you from monkey patching the MetadataStore with the missing Foreign Key property information.

EdmBuilder workaround

There is an approved workaround if your OData service is backed by the Entity Framework. You can use the Breeze Labs EdmBuilder to produced the metadata instead of the Web API's own ODataConventionModelBuilder. The "Web API OData and Breeze" sample explains why this is necessary and how to wire up the EdmBuilder.

We summarize here:

  1. Get "EdmBuilder.cs" from nuget with the command Install-Package Breeze.EdmBuilder. You can also download it from github. Put it in your Web API "App_Start" folder.

  2. Adjust your WebApiConfig.cs accordingly:

    config.Routes.MapODataRoute(
        routeName: "odata",
        routePrefix: "odata",
        model: EdmBuilder.GetEdm<MyDbContext>(),
        batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
    );

Correcting the namespace for EdmBuilder

Beware of model classes with a different namespace!

Many folks put their model classes in their own namespace, something other than the DbContext or EDMX namespace.

Unfortunately the EDM generator - which relies on Microsoft's own tooling - ignores the model namespace and "relocates" them for its purposes to either the DbContext namespace or the EDMX namespace (this is true of the ODataConventionBuilder too).

For example, if a "code first" DbContext namespace is "EF" and the model namespace is "Models", the Microsoft generated metadata describe the Customer class as an "EF.Customer" even though the Web API controller methods (properly) refer to that entity as "Models.Customer".

This mistake creates confusion on both server and client. The OData server is confused about the actual entity class types so it rejects client requests for data with the response: 406 - Not Acceptable.

How helpful is that message!?!

When the Breeze client reads the metadata, it's told to expect the "EF.Customer" entity type. But the JSON query results are typed as "Models.Customer". Breeze can't find that type and assumes that you haven't fetched metadata at all. It reports: "Unable to locate a 'Type' by the name: 'Customer:#EF'. Be sure to execute a query or call fetchMetadata first.". That's technically correct ... but not the actual cause of the problem.

We can suggest a server-side workaround, depending on how you constructed your DbContext.

You are stuck if your model classes have more than one namespace. As far as we know, Web API OData cannot handle model classes located in multiple namespaces.

Code First DbContext workaround

Tell the EdmBuilder to generate metadata from a derived DbContext that you've located in the same namespace as the model classes. Let's try it.

First we write a derived DbContext (we'll call it MyDbContextForEdm) in the "Models" namespace. We could put this class in any file. Let's put it in the WebApiConfig.cs because that's the only place we'll ever need it.

// WebApiConfig.cs
namespace Models {
    internal class MyDbContextForEdm : MyApp.EF.MyDbContext {}
}

Notice that it has no implementation. Its sole purpose is to relocate MyDbContext to the "Models" namespace. We can ignore it once it has helped with EDM generation.

Now we reference it in place of the base MyDbContext.

// WebApiConfig.cs
...
namespace MyApp {

    public static void Register(HttpConfiguration config) {
        ... 
        config.Routes.MapODataRoute(
            routeName: "odata",
            routePrefix: "odata",
            model: EdmBuilder.GetEdm<MyApp.Models.MyDbContextForEdm>(),
            batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
        );
        ...
    }
    ...
}

Did you see the change?

model: EdmBuilder.GetEdm<MyApp.Models.MyDbContextForEdm>(),

EDMX-based DbContext workaround

If you maintain your EF model "Database First" with an EDMX, you need a different workaround because the EdmBuilder acquires the namespace from the EDMX rather than the DbContext (whose namespace can by something else entirely).

Therefore you have to correct the EDMX namespace to match the model entity namespace. You can do that through the EDMX designer:

  • Open the EDMX in the designer (double-click it)
  • Open the "Properties" for the entire model
  • Change the "Namespace" property to the model entity namespace.

Here's that property sheet:

EDMX property editor

The EDMX namespace does not appear to play a practical role in an existing EF application. For example, changing it does not affect the namespaces of the corresponding entity classes.

Correcting the namespace for ODataConventionModelBuilder

What if you don't care about related entities?

You can use the ODataConventionModelBuilder if your model lacks entity relationships or your Breeze client application won't need them.

Once again, beware of model classes with a different namespace! The ODataConventionModelBuilder assumes that the model classes have the same namespace as your API controller. So when it generates metadata, it ignores the model classes' own namespace and specifies the controller's namespace instead.

The solution is to tell the ODataConventionModelBuilder to generate metadata for classes in the model's namespace, not the controller's namespace. Put code like this in your WebApiConfig.cs (located in the App_Start folder).

public static IEdmModel GetEdmModel()
{
    ODataModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<customer>('Customers');
    builder.Namespace = "Models";  // DON'T FORGET THIS!
    return builder.GetEdmModel();
}

public static void Register(HttpConfiguration config)
{
    ...
    config.Routes.MapODataRoute(
            routeName: "odata", 
            routePrefix: "odata",
            model: GetEdmModel(), 
            batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
            );
    ...
}

You are stuck if your model classes have more than one namespace. As far as we know, Web API OData cannot handle model classes located in multiple namespaces.

Is Web API OData right for you?

We've describe here some of the real-world challenges of Web API OData development. We discuss more of them in "OData vs. Web API". These are pretty serious obstacles for many applications.

Should you build a Breeze Web API service, a Conventional Web API service, or an OData Web API service? Here's how we see it.

First things first! A Breeze client can talk to any HTTP service you choose including any flavor of Web API: Breeze, Conventional, or OData.

A Breeze Web API server gives you the best combination of productivity, flexibility, power, and safety. There are no metadata namespace issues, no fundamental obstacles to transactions or your control over save order. The Breeze Web API server handles these for you when used with Entity Framework or NHIbernate.

You have the most flexibility when you write a Conventional Web API (no Breeze on the server) but you'll spend more time and money developing and maintaining that service.

On the downside, either the Breeze or conventional Web API path leads to an idiosyncratic API. That may not be the best approach if you need a public API. The Breeze API is "standard" for Breeze clients only; a custom Web API implementation is not standard at all.

The OData Web API solution yields an out-of-the-box OData API which is a public, widely recognized standard. That's a tempting choice if you need an open API with the widest reach.

On the other hand, it may be very difficult ... or effectively impossible ... to save changes properly with the OData Web API implementation as it exists today. That may not matter if your public API is read-only. But if clients can save data through the public API, we urge you to think carefully about your needs and proceed with caution.

How do I choose?

Pick the server that's best for you. As we said, a Breeze client application communicates equally well with all of them.

Perhaps you can take a hybrid approach: a Breeze Web API server for "internal" clients accompanied by a more restrictive OData Web API server for "public" clients. You won't have to write the server twice. Rather you wrap the same core server logic in two separate lightweight controller sets - one Breeze controller and one small, focused set of OData controllers. Each controller is a dispatcher, delegating real work to the core server logic in common. Each controller exposes the API that is appropriate for its target audience.

Contact breeze@ideablade.com to learn more about this option.

SharePoint OData

SharePoint 2010 and 2013 offer "OData REST APIs". They only partially comply with the OData spec and none of them return metadata that a Breeze client can consume.

Even if SP did produce "correct metadata", we rather doubt that you'd want it; a trivial data model produces almost 750Mb of metadata, most of it of no interest to the client application developer.

Your only recourse at this time is to define the metadata you need on the client. Learn more by starting with our SharePoint 2013 Sample.