Getting Started Managing Client-Side Data with the Breeze JavaScript Library

If you work with JavaScript a lot then you know that there’s no shortage of script libraries being released. New scripts come out on a daily basis providing UI functionality, data binding support, form validation, framework functionality and more. With all of the scripts in circulation it feels like nearly every aspect of client-side development has been covered. However, there’s one area that has seen precious few scripts – the area of client-side data management.

If you need to query local caches of data, track dirty objects, send modified objects to the server as a batch or validate in-memory data, then you’ve probably been writing custom code or cobbling together various script libraries to solve the problem. Fortunately, a new script library named Breeze has been released that can help with these challenges and more.

In this post I’ll provide an overview of how to get started using Breeze and discuss some of the data management features that it offers client-side developers. Let’s start by taking a quick look at some of the features that Breeze offers.

Breeze Features

Breeze (http://breezejs.com) is a free open source library from IdeaBlade that aims to solve many of the data management challenges that developers face when building SPAs and other client-centric applications. Although it’s a relatively new library, it provides robust functionality out of the box such as client-side caching, change tracking, entity validation, a variety of data management features (support for custom metadata, import/export entities, entity ID strategies, support for multiple caches, etc.), rich entity query support (both local and remote) directly from JavaScript, and batch saves of entities to a server.

In addition to these features, Breeze is designed to integrate with many of the popular libraries out there such as KnockoutJS, AngularJS, Backbone and more. If you’re already using KnockoutJS to handle client-side data binding then you can plug-in Breeze anywhere you’re currently retrieving, querying, or manipulating data and still keep all of the existing KnockoutJS functionality intact. The same can be said when using the other libraries mentioned.

Breeze can also integrate with different back end server technologies and doesn’t force you to use a particular framework. Out of the box it provides support for ASP.NET Web API and Entity Framework so if you’re already using ASP.NET MVC 4 you can get started quickly. A different back end framework such as Node.js can also be used although you’d have to write the code to handle serving data to the client and processing data received from the client.

Now that you’ve seen some of the general features that Breeze offers let’s take a look at how you can get started using it. Although I’ll use Visual Studio and NuGet in the examples that follow, any editor can be used.

Getting Started with Breeze

One of the best ways to get started with Breeze is to load a sample application into an empty ASP.NET MVC 4 project using NuGet. To do that, right-click on the project References, select Manage NuGet Packages, and then click the Online option to the left of the dialog window. In the Search Online box type Breeze. Once the search results come back select the Breeze for ASP.NET MVC4 Web Api Client Sample package and select Install. Here's an example of the NuGet dialog. The Breeze site also has a video walk-through of getting started with the NuGet package at http://www.breezejs.com/documentation/start-NuGet if you're new to it.

image

 

Once the NuGet package loads a readme.txt file is displayed in Visual Studio 2012 that walks through the application. To get started with it you can simply press F5 to run the sample. Once the application loads in the browser you'll see a screen similar to the one shown next:

clip_image003

 

You can edit tasks, mark tasks as done, and save changes. Anything you change gets pushed back to the server using the Breeze library and is handled by an ASP.NET Web API controller class (more on this later). Key server-side classes added into the project by the Breeze NuGet package include BreezeClientSampleConfig which handles default routing for the application, BreezeWebApiConfig which handles ASP.NET Web API routing, BreezeSampleController which acts as the ASP.NET Web Api controller and BreezeSampleShellController which handles serving up the initial view and HTML to the browser.

Key client-side files added into the project include breeze.debug.js (the debug version of the Breeze script), breeze.js (the production version), logger.js and sampleViewModel.js. The sampleViewModel.js script relies on KnockoutJS to provide data binding capabilities. Here are the files that you'll see in the Solution Explorer:

clip_image004

 

Now that a Breeze project has been created let's take a closer look at some of the code that's used on the server-side.

The Server-Side Code

The BreezeSampleController class derives from the standard ApiController class and provides ASP.NET Web Api controller support for sending data to and from a client. It includes a Breeze-specific class named EFContextProvider<T> that wraps a custom Entity Framework Code First DbContext class named BreezeSampleContext. The code for the BreezeSampleController and BreezeSampleContext classes are shown in Listings 1 and 2 respectively. It's important to note that although the sample application relies on specific Breeze server-side objects such as EFContextProvider<T>, Breeze is designed to work with a variety of server-side frameworks.


[BreezeController]
public class BreezeSampleController : ApiController {

    readonly EFContextProvider<BreezeSampleContext> _contextProvider =
        new EFContextProvider<BreezeSampleContext>();

    [HttpGet]
    public string Metadata() {
        return _contextProvider.Metadata();
    }
        
    [HttpPost]
    public SaveResult SaveChanges(JObject saveBundle) {
        return _contextProvider.SaveChanges(saveBundle);
    }
        
    [HttpGet]
    public IQueryable<BreezeSampleTodoItem> Todos() {
        return _contextProvider.Context.Todos;
    }

}

Listing 1. The BreezeSampleController class that acts as the ASP.NET Web API controller for the sample application.

 

public class BreezeSampleContext : DbContext 
{
    // DEVELOPMENT ONLY: initialize the database
    static BreezeSampleContext()
    {
        Database.SetInitializer(new BreezeSampleDatabaseInitializer());
    }    
    public DbSet<BreezeSampleTodoItem> Todos { get; set; }
}

Listing 2. The BreezeSampleContext handles dynamically initializing the database and defines a Todos property used by the application to display Todo data.


Looking at the code in Listing 1 you can see that a readonly field named _contextProvider of type EFContextProvider<BreezeSampleContext> is defined in the controller class. Because the service relies on Entity Framework for data access, this ContextProvider wraps an instance of an Entity Framework (EF) context. It extracts metadata from the EF model that the client needs and translates client save requests into EF save operations after applying server-side business logic to authorize and validate those changes. It also exposes the DbContext’s Todos property, an IQueryable that can be filtered, sorted, and paged before returning BreezeSampleTodoItem objects (see Listing 3) to the client.

 

public class BreezeSampleTodoItem
{
    public int Id { get; set; }             // 42
    public string Description { get; set; } // "Try Breeze"
    public bool IsDone { get; set; }        // false
}

Listing 3. The BreezeSampleTodoItem class.

To see the metadata returned by the Metadata() action found in BreezeSampleController you can open your browser and navigate to the following path (note that you'll have to change the port to match the port assigned to your application):

http://localhost:55143/api/BreezeSample/metadata

The metadata describes important aspects of the model that is used by the client-side portion of Breeze such as the entity types, each type’s property names, their data types, key properties, and the relationships among entities.

Now that you've seen the Web API controller class let's switch gears and look at the client-side code that interacts with the Web Api controller and see how Breeze fits into the overall picture.

The Client-Side Code

The Breeze sample application contains a script in the Scripts/app folder named sampleViewModel.js that contains the main client-side Breeze functionality. It handles retrieving metadata and Todo objects from the server and then binds the Todo objects into the page using KnockoutJS. As changes are made it uses Breeze to batch up changes and then send them back to the server for processing. Keep in mind that in a more complex application the view model may call out to a separate BreezeJS JavaScript object that focuses specifically on data functionality. That helps promote re-use in situations where multiple view models need to access and manipulate data through Breeze.

The sampleViewModel.js script starts by creating a new Breeze EntityManager object that targets the Web Api controller discussed earlier. The KnockoutJS view model relies on the entity manager for queries and saves  (see Listing 4).

 

// service name is route to the Web API controller
var serviceName = 'api/BreezeSample';

// manager is the service gateway and cache holder
var manager = new breeze.EntityManager(serviceName);

// define the viewmodel
var vm = {
    todos: ko.observableArray(),
    includeDone: ko.observable(false),
    save: saveChanges,
    show: ko.observable(false)
};

// start fetching Todos
getTodos();

// re-query when "includeDone" checkbox changes
vm.includeDone.subscribe(getTodos);

// bind view to the viewmodel
ko.applyBindings(vm);


Listing 4
. Creating an EntityManager to handle interacting with the server.



All data interaction goes through the EntityManager object including queries to the server, sending saved batches of data to the server and more. To query data from the server an EntityQuery object must be created and then executed by the EntityManager. Listing 5 shows an example of creating an EntityQuery object, executing the query and handling the results.

// get Todos asynchronously
// returning a promise to wait for     
function getTodos() {

    logger.info("querying Todos");

    var query = breeze.EntityQuery.from("Todos");

    if (!vm.includeDone()) {
        query = query.where("IsDone", "==", false);
    }

    return manager
        .executeQuery(query)
        .then(querySucceeded)
        .fail(queryFailed);

    // reload vm.todos with the results 
    function querySucceeded(data) {
        logger.success("queried Todos");
        vm.todos(data.results);
        vm.show(true); // show the view
    }
};

Listing 5. Executing a query against the ASP.NET Web API controller using the Breeze EntityManager and EntityQuery objects.


The code starts by identifying which method to query on the server-side controller (Todos in this example), creates a dynamic query, and then executes it. If the view model's includeDone property is false then a dynamic WHERE clause is added that will be sent to the server when querying Todo objects. The ability to write client-side queries that ultimately make their way to the server is a unique feature of Breeze and something I've personally wanted for quite a while for client-side application development. More complex queries can be written as well using the EntityQuery object combined with a Predicate object as shown in Listing 6 (see http://www.breezejs.com/documentation/query-examples#Composite Where clauses for additional examples).

// Start with a base query for all Todos 
var query = breeze.EntityQuery.from("Todos"); 

// A Predicate is a condition that is true or false
// Combine two predicates with ".and" to
// query for Todos that are done and
// the description contains the letter 's'
var p1 = new breeze.Predicate("IsDone", "eq", true);
var p2 = new breeze.Predicate("Description", "contains", "s");
query = query.where(p1.and(p2));

return manager.executeQuery(query);


Listing 6. Creating queries using the EntityQuery and Predicate objects. The queries can be used to query client-side or server-side objects.


In addition to querying objects on the server, the EntityManager can also save changes made on the client back to the server. When the user clicks the “save” link, the view model calls the EntityManager’s saveChanges() function. The EntityManager batches up all pending changes and sends them back to the controller for processing. Listing 7 shows how the EntityManager's saveChanges() function can be invoked and used to detect success or failure using promises.

 

function saveChanges() {
    return manager.saveChanges()
        .then(function () { logger.success("changes saved"); })
        .fail(saveFailed);
}

function saveFailed(error) {
    logger.error("Save failed: " + error.message);
}

Listing 7. Using Breeze's EntityManager to save changes made on the client back to the server.

 

Conclusion

Breeze fills a huge hole in client-side development through its data management capabilities. By providing query capabilities, batch save functionality, and the ability to perform a variety of other features it greatly simplifies the process of building CRUD-style (create, read, update, delete) applications and allows developers to focus on application logic as opposed to data plumbing code. Find out more information about Breeze by visiting the website at http://www.breezejs.com.

comments powered by Disqus

No Comments