Working with Breeze Lookup Queries

One capability of Breeze that is often overlooked or not fully understood is executing queries that bring back multiple collections of data. These are often referred to as lookup queries because they are a perfect fit for going to the server one time to get back a collection of lists that can be used for populating “lookup lists” – lists of objects that you will use to populate drop down lists or other selection lists in various places in the application. These are often coded lists of allowable values for an enumerated property on some object you are going to be editing, and are usually fairly invariant over time. But you could also use this capability to retrieve multiple collections of interrelated objects in a single shot if you can not achieve the same result through a normal query execution with filtering and expansion.

To execute a lookup query, the server needs to expose an API operation that return a JSON payload with a root object containing each of the collections as a named property. If you were implementing this with an ASP.NET BreezeController, the controller method would look like this:

1: [BreezeController]

2:publicclass ZzaController : ApiController

3: {

4: EFContextProvider _ContextProvider = new EFContextProvider();

5:

6: [HttpGet]

7:publicstring Metadata()

8: {

9:return _ContextProvider.Metadata();

10: }

11:

12: [HttpGet]

13:publicobject Lookups()

14: {

15: var orderStatuses = _ContextProvider.Context.OrderStatuses.ToList();

16: var productOptions = _ContextProvider.Context.ProductOptions.ToList();

17: var productSizes = _ContextProvider.Context.ProductSizes.ToList();

18:returnnew { orderStatuses, productOptions, productSizes };

19: }

20:

21: }



The resulting wire level format will be a single JSON object at the root of the payload, containing one or more named array properties that contain the lists. For the server method above, addressed as /breeze/Zza/Lookups, the response body would look roughly like this:

{ $id=1, $type=…, orderStatuses = […], productOptions […], productSizes =[…] }

The individual items in the arrays would need to be entity types that are included in the client side metadata and that express their type with additional metadata properties on the returned JSON:

{ $id=2, $type=’Zza.Data.OrderStatus, Zza.Data’, Id = 1, Name=’Ordered’ }

If using the Breeze ASP.NET Web API support to implement BreezeControllers with EntityContextProviders, to get this extra type information associated with the entities you just need to expose a DbSet property from your DbContext for each of the entity types in the collections you return, but you do not need to have a separate query method for those types exposed from your Web API Controller.

1:publicclass ZzaDbContext : DbContext

2: {

3:public DbSet ProductOptions { get; set; }

4:public DbSet ProductSizes { get; set; }

5:public DbSet OrderStatuses { get; set; }

6: }



To execute the lookup query, you do it like any other query execution with Breeze, you just use the name of your server API operation in specifying the query:

1:var em = new breeze.EntityManager('breeze/Zza');

2:var lookupsQuery = breeze.EntityQuery.from('Lookups');

3: em.executeQuery(lookupsQuery).then(successHandler, errorHandler);



The important part to understand is what you will see in the successHandler arguments vs. what will be created in the EntityManager cache. When your successHandler gets called, like for other Breeze queries, the data object that gets passed to your handler will contain a results array just like it does for any other query, but the results will have the single object returned in it. That single item in the array will be the single root object that the server side returned. And that object, as expressed by the server will have the named properties containing each of the arrays returned by the server.

What sometimes trips people up is understanding how that relates to what gets created in the cache so that you can query for those collections in the cache later. The thing to realize is that the collection names produced in the cache have nothing to do with the names of the properties for the collections in the server payload. In the example payload I showed above, I could have named the properties foo, bar, and baz instead of orderStatuses, productOptions, and productSizes. And if I did that, those would still be the names of the properties containing the arrays when received in the client executeQuery successHandler.

But if the types of those entities were OrderStatus, ProductOption, and ProductSize, the names of the resulting collections in the EntityManager cache after the query completes would still be OrderStatuses, ProductOptions, and ProductSizes respectively. So if you were going to write code to retrieve those entities from the cache later, the collection name you would want to use when creating an EntityQuery to query the local cache with executeQueryLocally would be the plural of the entity type name, not the name of the property that contained the collection in the server payload.

Here is client JavaScript that demonstrates that the name of the array property in the successHandler is different from the collection created in the EntityManager cache for the Lookups controller showed earlier, if you changed the orderStatuses local variable name in the server Lookups method to foo:

1:var em = new breeze.EntityManager('breeze/Zza');

2:var lookupsQuery = breeze.EntityQuery.from('Lookups');

3: em.executeQuery(lookupsQuery).then(successHandler, errorHandler);

4:

5:function successHandler(data) {

6:var results = data.results[0];

7:if (results.foo != null)

8: console.log("results contain foo collection");

9:var cacheResults = em.executeQueryLocally(breeze.EntityQuery.from("OrderStatuses"));

10:if (cacheResults.length > 0)

11: console.log("cache contains 'OrderStatuses' collection");

12: }



Notice that the data.results[0] object is the root object returned by the Lookups method, and that still has a foo property for the array of OrderStatus objects. But the collection that is created in the EntityManager cache that you can query for is not based on the names of those arrays in the payload object, they are based on the type of the entities contained in the arrays – in this example OrderStatus. So the resulting EntityManager collection is OrderStatuses.

Hopefully that helps clarify the differences between what you see in the query completion results and what gets put in the cache. If you have questions, ping me on Twitter @briannoyes.