Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Breezejs EntityManager MetadataStore and fetchEntityByKey

I have a SPA application (durandaljs), and I have a specific route where I map the "id" of the entity that I want to fetch.

The template is "/#/todoDetail/:id".

For example, "/#/todoDetail/232" or "/#/todoDetail/19".

On the activate function of viewmodel, I get the route info so I can grab the id. Then I create a new instance of breezejs EntityManager to get the entity with the given id.

The problem is when I call manager.fetchEntityByKey("Todos", id), the EntityManager doesn't have yet the metadata from the server, so it throwing exception "Unable to locate an 'Type' by the name: Todos".

It only works if first I execute a query against the store (manager.executeQuery), prior to calling fetchEntityByKey.

Is this an expected behavior or a bug ? Is there any way to auto-fecth the metadata during instantiation of EntityManager ?

note: I believe it's hard to use a shared EntityManager in my case, because I want to allow the user directly type the route on the browser.

EDIT: As a temporary workaround, I'm doing this:

BreezeService.prototype.get = function (id, callback) {
    var self = this;

    function queryFailed(error) {
        app.showMessage(error.message);
        callback({});
    }

    /* first checking if metadatastore was already loaded */

    if (self.manager.metadataStore.isEmpty()) {
        return self.manager.fetchMetadata()
        .then(function (rawMetadata) {
            return executeQuery();
        }).fail(queryFailed);
    } else {
        return executeQuery();
    }

    /* Now I can fetch */
    function executeQuery() {
        return self.manager.fetchEntityByKey(self.entityType, id, true)
                        .then(callback)
                        .fail(queryFailed);
    }
};
like image 947
Jone Polvora Avatar asked Feb 10 '13 23:02

Jone Polvora


2 Answers

You've learned about fetchMetadata. That's important. If you application can begin without issuing a query, you have to use fetchMetadata and wait for it to return before you can perform any operations directly on the cache (e.g., checking for an entity by key in the cache before falling back to a database query).

But I sense something else going on because you mentioned multiple managers. By default a new manager doesn't know the metadata from any other manager. But did you know that you can share a single metadataStore among managers? You can.

What I often do (and you'll see it in the metadata tests in the DocCode sample), is get a metadataStore for the application, write an EntityManager factory function that creates new managers with that metadataStore, and then use the factory whenever I'm making new managers ... as you seem to be doing when you spin up a ViewModel to review the TodoDetail.

like image 141
Ward Avatar answered Nov 05 '22 23:11

Ward


Coming from a Silverlight background where I used a lot of WCF RIA Services combined with Caliburn Micro, I used this approach for integrating Breeze with Durandal.

I created a sub folder called services in the App folder of the application. In that folder I created a javascript file called datacontext.js. Here is a subset of my datacontext:

define(function (require) {

    var breeze = require('lib/breeze'); // path to breeze
    var app = require('durandal/app');  // path to durandal

    breeze.NamingConvention.camelCase.setAsDefault();

    // service name is route to the Web API controller
    var serviceName = 'api/TeamData',

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

    store = manager.metadataStore;

    function queryFailed(error) {
        app.showMessage("Query failed: " + error.message);
    }

    // constructor overrides here

    // included one example query here
    return datacontext = {
        getSponsors: function (queryCompleted) {
            var query = breeze.EntityQuery.from("Sponsors");
            return manager
                .executeQuery(query)
                .then(queryCompleted)
                .fail(queryFailed)
        }
    };
}

Then in your durandal view models you can just require the services/datacontext. For example, here is part of a sample view model from my app:

define(function (require) {

    var datacontext = require('services/datacontext');

    var ctor = function () {
        this.displayName = 'Sponsors',
        this.sponsors = ko.observable(false)
    };

    ctor.prototype.activate = function () {
        var that = this;
        return datacontext.getSponsors(function (data) { that.sponsors(data.results) });
    }

    return ctor;
});

This will allow you to not worry about initializing the metadata store in every view model since it is all done in one place.

like image 40
Bryant Avatar answered Nov 05 '22 22:11

Bryant