3
votes

If I use breeze to load a partial entity:

    var query = EntityQuery.from('material')
        .select('Id, MaterialName, MaterialType, MaterialSubType')
        .orderBy(orderBy.material);

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

        function querySucceeded(data) {
            var list = partialMapper.mapDtosToEntities(
                manager, data.results, entityNames.material, 'id');
            if (materialsObservable) {
                materialsObservable(list);
            }
            log('Retrieved Materials from remote data source',
                data, true);
        }

...and I also want to have another slightly different partial query from the same entity (maybe a few other fields for example) then I'm assuming that I need to do another separate query as those fields weren't retrieved in the first query?

OK, so what if I want to use the same fields retrieved in the first query (Id, Materialname, MaterialType, MaterialSubType) but I want to call those fields different names in the second query (Materialname becomes just "name", MaterialType becomes "masterType" and so on) then is it possible to clone the partial entity I already have in memory (assuming it is in memory?) and rename the fields or do I still need to do a completely separate query?

1
Are you trying to have the projections mapped back to the original entities or just get a subset of data for display purposes?PW Kad
I'm not really sure of the terminology! I am using the original projection query in a knockout binding which works fine. I need exactly the same data but just with different field names for a 3rd party control (a treeview) on the page as this needs field names such as "Id" and "name" among others to work properly.TheMook
I've been searching the breeze site all afternoon trying to find information on using alias names in a query. Even if I did just a separate projection query for my treeview data I don't see how I can "select MaterialName as Name" for example!TheMook
Maybe this makes more sense - are you wanting to have the data just be displayed and not edit it or anything or use the data in this query to edit the original values?PW Kad
Yes, that makes more sense. I'm just displaying it. It's being used for a treeview.TheMook

1 Answers

2
votes

I think I would "union" the two cases into one projection if I could afford to do so. That would simplify things dramatically. But it's really important to understand the following point:

You do not need to turn query projection results into entities!

Backgound: the CCJS example

You probably learned about the projection-into-entities technique from the CCJS example in John Papa's superb PluralSight course "Single Page Apps JumpStart". CCJS uses this technique for a very specific reason: to simplify list update without making a trip to the server.

Consider the CCJS "Sessions List" which is populated by a projection query. John didn't have to turn the query results into entities. He could have bound directly to the projected results. Remember that Knockout happily binds to raw data values. The user never edits the sessions on that list directly. If displayed session values can't change, turning them into observable properties is a waste of CPU.

When you tap on a Session, you go to a Session view/edit screen with access to almost every property of the complete session entity. CCJS needs the full entity there so it looks for the full (not partial) session in cache and, if not found, loads the entity from the server. Even to this point there is no particular value in having previously converted the original projection results into (partial) session entities.

Now edit the Session - change the title - and save it. Return to the "Sessions List"

Question

How do you make sure that the updated title appears in the Sessions List?

If we bound the Sessions List HTML to the projection data objects, those objects are not entities. They're just objects. The entity you edited in the session view is not an object in the collection displayed in the Sessions List. Yes, there is a corresponding object in the list - one that has the same session id. But it is not the same object.

Choices

#1: Refresh the list from the server by reissuing the projection query. Bind directly to the projection data. Note that the data consist of raw JavaScript objects, not entities; they are not in the Breeze cache.

#2: Publish an event after saving the real session entity; the subscribing "Sessions List" ViewModel hears the event, extracts the changes, and updates its copy of the session in the list.

#3: Use the projection-into-entity technique so that you can use a session entity everywhere.

Pros and Cons

#1 is easy to implement. But it requires a server trip every time you enter the Sessions List view.

One of the CCJS design goals was that, once loaded, it should be able to operate entirely offline with zero access to the server. It should work crisply when connectivity is intermittent and poor.

CCJS is your always-ready guide to the conference. It tells you instantly what sessions are available, when and where so you can find the session you want, as you're walking the halls, and get there. If you've been to a tech conference or hotel you know that the wifi is generally awful and the app is almost useless if it only works when it has direct access to the server.

#1 is not well suited to the intended operating environment for CCJS.

The CCJS Jumpstart is part way down that "server independent" path; you'll see something much closer to a full offline implementation soon.

You'll also lose the ability to navigate to related entities. The Sessions List displays each session's track, timeslot and room. That's repetitive information found in the "lookup" reference entities. You'll either have to expand the projection to include this information in a "flattened" view of the session (fatter payload) or get clever on the client-side and patch in the track, timeslot and room data by hand (complexity).

#2 helps with offline/intermittent connectivity scenarios. Of course you'll have to set up the messaging system, establish a protocol about saved entities and teach the Sessions List to find and update the affected session projection object. That's not super difficult - the Breeze EntityManager publishes an event that may be sufficient - but it would take even more code.

#3 is good for "server independence", has a small projection payload, is super-easy, and is a cool demonstration of breeze. You have to manage the isPartial flag so you always know whether the session in cache is complete. That's not hard.

It could get more complicated if you needed multiple flavors of "partial entity" ... which seems to be where you are going. That was not an issue in CCJS.

John chose #3 for CCJS because it fit the application objectives.

That doesn't make it the right choice for every application. It may not be the right choice for you.

For example, if you always have a fast, low latency connection, then #1 may be your best choice. I really don't know.

I like the cast-to-entity approach myself because it is easy and works so well most of the time. I do think carefully about that choice before I make it.

Summary

  1. You do not have to turn projection query results into entities
  2. You can bind to projected data directly, without Knockout observable properties, if they are read-only
  3. Make sure you have a good reason to convert projected data into (partial) entities.

CCJS has a good reason to convert projected query data into entities. Do you?