Consuming an ASP.NET Web API OData Service with Breeze

One of the last things I figured out to include in my Pluralsight course “Building ASP.NET Web API OData Services” (which should be out in a week or two – all done, just waiting for the editorial process to complete) was how to consume my Web API OData services with Breeze.js.

Breeze is a great JavaScript library for including rich service-based data access in HTML 5 / JavaScript applications. It acts as a smart client side repository that can make the retrieve and update service calls for you, caches the data client side so you can avoid unnecessary round trips, does change tracking on the entities so that it knows what to send back for update when you as it to (following a unit-of-work pattern), integrates nicely with Knockout (or other observable-based libraries) and more. Its also capable of consuming both Breeze Web APIs that you can easily set up with their ASP.NET Web API server libraries, or OData services (regardless of what platform those are on).

When it comes to integrating with OData, for now Breeze just supports querying (retrieval) data, not updating through the OData mechanisms. But it provides a nice abstraction layer for doing so because it lets you formulate LINQ-like syntax in your JavaScript code for things like “where” (filter operations at an OData wire level), orderby, top, skip and so on.

So it was a natural fit for the module in my course on consuming OData from .NET and JavaScript clients. Most of getting it working was totally straightforward, I just followed the great documentation on the Breeze site on setting up the Breeze configuration for OData and making the calls. There was one trick that I sorted out with the help of a little code that Ward Bell and the Web API team had been slinging around. Specifically, take note of the comments in Step 4 below about matching up the EDM namespace and the entity type namespace.

So here is a quick walkthrough so others can just take it by the numbers.

Step 1: Create your Web API Project

File > New Project, Web category, MVC 4 Application, select Web API project type.

Figure1

Figure2

Delete the ValuesController that it puts in the \Controllers folder.

Step 2: Add the OData NuGet

Right click on the project in Solution Explorer, find Manage NuGet Packages.

Figure3

Search for OData and find the ASP.NET Web API OData package.

Figure4

Step 3: Define a Data Model

I’m gonna use the venerable Northwind Customers table here to keep it simple. Add a connection string to your web.config:

   1: <add name="NorthwindDbContext" connectionString="server=.;

   2: database=Northwind;trusted_Connection=true" 

   3: providerName="System.Data.SqlClient"/>

Add a Customers class to the Model folder:

   1: public class Customer

   2: {

   3:     public string CustomerID { get; set; }

   4:     public string CompanyName { get; set; }

   5:     public string Phone { get; set; }

   6: }

And a NorthwindDbContext class (Entity Framework code first approach – could use DB First or any model you want).

   1: public class NorthwindDbContext : DbContext

   2: {

   3:     public DbSet<Customer> Customers { get; set; }

   4: }

Step 4:  Define an EDM

Open the App_Start folder in the project, open WebApiConfig.cs. Add a helper method to define an Entity Data Model:

   1: public static IEdmModel GetEdmModel()

   2: {

   3:     ODataModelBuilder builder = new ODataConventionModelBuilder();

   4:     builder.EntitySet<Customer>("Customers");

   5:     builder.Namespace = "WebAPIODataWithBreezeConsumer.Models";

   6:     return builder.GetEdmModel();

   7: }

The ODataConventionModelBuilder just needs to know the collections you want to expose. It will reflect on the types, and declare all their properties in the EDM with corresponding EDM types, will follow navigation properties to related entities and declare those, and will infer what the entity keys are.

IMPORTANT: Note line 5 – you need to make sure the namespace of the EDM matches the namespace your entity types are contained in.

Step 5: Add an OData Route

In the Register method, add an OData route for your EDM (line 3 below):

   1: public static void Register(HttpConfiguration config)

   2: {

   3:     config.Routes.MapODataRoute("odata", "odata", GetEdmModel());

   4:     config.Routes.MapHttpRoute(

   5:         name: "DefaultApi",

   6:         routeTemplate: "api/{controller}/{id}",

   7:         defaults: new { id = RouteParameter.Optional }

   8:     );

   9: }

The MapODataRoute extension method takes three arguments: a name for the route, a prefix off the site root address where you can get to the EDM model you will expose, and the EDM model itself.

Step 6: Create an EntitySetController

Add a project folder called ODataControllers. Add a class in there called CustomersController, with a base class of EntitySetController<Customer,string>. Add a Get method to it that uses the NorthwindDbContext to return Customers:

   1: public class CustomersController : EntitySetController<Customer,string>

   2: {

   3:     NorthwindDbContext _Context = new NorthwindDbContext();

   4:  

   5:     [Queryable]

   6:     public override IQueryable<Customer> Get()

   7:     {

   8:         return _Context.Customers;

   9:     }

  10:  

  11:     protected override void Dispose(bool disposing)

  12:     {

  13:         base.Dispose(disposing);

  14:         _Context.Dispose();

  15:     }

  16: }

Note the [Queryable] attribute on the Get method – this in combination with the IQueryable return type is what enables OData query syntax. The EntitySetController takes care of making sure the OData formatter is used instead of the default JSON formatter, as well as taking care of mapping OData routing scheme to this controller. The default OData format is JSON-Light (as opposed to JSON-Verbose or ATOM XML).

At this point you have a working OData service. You should be able to hit it in the browser with an address of /odata/Customers and you will get back the customers in the default JSON-Light format. If you want ATOM or JSON-Verbose, you will need to use a tool like Fiddler where you can control the headers.

Figure5

Step 7: Add DataJS and Breeze.js through NuGet

Go back into Manage NuGet Packages from Solution Explorer and search for Breeze. Select the Breeze for ASP.NET Web Api Projects package and install. Then do the same to find and install DataJS.

Figure6

Step 8: Create an HTML page + JS with JQuery, Knockout, Q, and Breeze

Go into the Views folder of your Web project and find the /Home/Index.cshtml file. Clear out all the HTML in there and add the following scripts to the top (Note: you can do this by dragging and dropping the script file from the /scripts folder (you may need to update the JQuery and Knockout NuGet packages in your project to match these versions).

   1: <script src="~/Scripts/jquery-1.9.1.js"></script>

   2: <script src="~/Scripts/knockout-2.2.1.js"></script>

   3: <script src="~/Scripts/q.js"></script>

   4: <script src="~/Scripts/datajs-1.1.0.js"></script>

   5: <script src="~/Scripts/breeze.debug.js"></script>

Next we need to start building the JavaScript that will use Breeze to execute a query and put it in a view model we can bind to. First step is to initialize Breeze and tell it we will be dealing with OData:

 

   1: <script type="text/javascript">
   1:  

   2:     var my = {}; //my namespace

   3:     $(function () {

   4:         var serverAddress = "/odata/";

   5:         breeze.config.initializeAdapterInstances({ dataService: "OData" });

   6:         var manager = new breeze.EntityManager(serverAddress);

   7:     });

</script>

Next we need a simple view model that we will data bind to from our HTML, declared inside the script block:

 

   1: my.vm = {

   2:     customers: ko.observableArray([]),

   3:     load: function () {

   4:     }

   5: }

 

Next the code that calls the load and sets up the data binding with Knockout at the end of our script block, inside the JQuery ready function:

   1: my.vm.load();

   2: ko.applyBindings(my.vm);

Next some simple Knockout bound HTML to render the customers (outside the script block obviously):

   1: <h2>Customers (<span data-bind="text: customers().length"></span>)</h2>

   2: <table>

   3:     <thead>

   4:         <tr>

   5:              <th>CustomerID</th>

   6:              <th>CompanyName</th>

   7:              <th>Phone</th>

   8:         </tr>

   9:     </thead>

  10:     <tbody data-bind="foreach: customers">

  11:         <tr >

  12:             <td data-bind="text: CustomerID"></td>

  13:             <td data-bind="text: CompanyName"></td>

  14:             <td data-bind="text: Phone"></td>

  15:         </tr>

  16:     </tbody>

  17: </table>

Step 9: Query Away!

Now all we have to do is flesh out our load method in the view model. All we need to do is form a query and execute it with the Breeze EntityManager, putting the results where we want them – in this case I loop over the results in a success callback function, pushing them into my observable array in my view model. The really cool thing is that Breeze takes care of downloading the OData metadata for the Entity Data Model and automatically creates observable types for the data objects, so the individual objects I am adding into my array are data binding friendly JS types with observable properties.

   1: load: function () {

   2:     var query = breeze.EntityQuery.from("Customers");

   3:     manager.executeQuery(query, function (data) {

   4:         var results = data.results;

   5:         $.each(data.results, function (i, c) {

   6:             my.vm.customers.push(c);

   7:         });

   8:     });

   9: }

 

We can now run and we should get all customers:

Figure7

The other cool thing is that now we can start formulating more complicated LINQ-like queries with Breeze. instead of just asking for all customers above, we could change it to ask for customers who’s company name starts with B and order by the CustomerID. Just change line 2 above to:

 

   1: var query = breeze.EntityQuery.from("Customers")

   2: .where("CompanyName","startsWith","B").orderBy("CustomerID");

 

And viola:

Figure8

Summary

Web API makes exposing OData services a piece of cake and gives you more power and flexibility to integrate your own business logic and data access than you get in WCF Data Services. Breeze makes a great JavaScript client library to query those services, allowing you to write LINQ like code to formulate your queries. Of course Breeze goes way beyond just letting you query – it gives you a really rich experience for binding, editing, validating, going offline, and then pushing changes to the data back to the server. But those things will have to wait for another post…

Please check out my Pluralsight Course “Building ASP.NET Web API OData Services” (due out by end of February) for the full story on Web API and OData.

You can download the full source code for this sample here.

Comments are closed.