Metadata by hand

Sometimes you can't generate Breeze metadata or don't want to do so. This topic explains how to write the metadata yourself in JavaScript.

It's not hard

We know it seems daunting at first, especially if you've seen the server response from a metadata request. Perhaps you've examined the output from entityManager.metadataStore.exportMetadata or rooted around a MetadataStore in the debugger.

What you're seeing is a representation of metadata meant for a machine ... not for a human. While it's sort of readable in JSON, no one would want to write that ... and we don't expect you to write that.

We'll show you how authoring metadata with the Breeze Labs Metadata-Helper can be easy and intuitive.

The Metadata-Helper is a domain-specific language (DSL) that strives to minimize the information you have to provide when writing Breeze metadata. It relies on common conventions to make good guesses about the details of your model's types, properties and associations. If it guesses incorrectly about a particular point (or can't guess at all), you can configure that specific point explicitly.

The Breeze Labs MetadataHelper extension is defined in breeze.metadata-helper.js. Download it from GitHub and install it on your page after loading breeze.

It is also available as a NuGet package: Install-Package Breeze.Metadata.Helper

In this topic, we'll concentrate on using the Metadata-Helper when it gets it right ... which is most of the time.

The related topic, "Metadata by hand (in depth)" delves into the details of how the helper works, how you can intervene, and how it all relates to the underlying metadata schema that Breeze uses at runtime. Visit that page when you're ready ... if you have to.

Show me

There is a live code example in this plunker; look at the metadataFactory.js script. Meanwhile, we continue here with a different example.

Let's look at the JavaScript metadata for the "Code Camper" model from John Papa's Plural Sight course, "Building Data-Centric Single Page Applications with Breeze".

The Model

The model has five entity types:

  1. Session - a talk delivered at a code camp or a technical conference
  2. Person - a person who attends the code camp
  3. Room - where the talk is delivered
  4. TimeSlot - session time slots in the conference schedule with start and duration
  5. Track - a conference track such as "JavaScript", "Data", "Cloud", "Mobile"

These entities are related. A Session has a Room, TimeSlot, Track, and a Speaker (a Person who delivers the talk). You should be able to write session.room, session.track, session.speaker, etc.

A Speaker may give more than one talk so the Person type has a collection of one or more Sessions which you access by writing person.speakerSessions.

What you write

Here is the metadata you might write to describe that model.

function fillMetadataStore(metadataStore) {
    store = metadataStore;

     // Add types in alphabetical order ... because we can
    addPerson();
    addSession();
    addRoom();
    addTimeSlot();
    addTrack();
}

function addPerson() {
    addType({
        name: 'Person',
        dataProperties: {
            id:          { type: ID },
            firstName:   { max: 50, nullOk: false },
            lastName:    { max: 50, nullOk: false },
            email:       { max: 400 },
            blog:        { max: 400 },
            twitter:     { max: 400 },
            gender:      { max: 1 },
            imageSource: { max: 400 },
        },

        navigationProperties: {
            speakerSessions: { type: 'Session', hasMany: true }
        }
    });
}

function addSession() {
    addType({
        name: 'Session',
        dataProperties: {
            id:           {type: ID },
            title:        {max: 50, nullOk: false},
            code:         {max: 10 },
            description:  {max: 4000 },
            level:        {max: 30 },
            tags:         {max: 4000 },

            roomId:       {type: ID, nullOk: false},
            speakerId:    {type: ID, nullOk: false},
            timeSlotId:   {type: ID, nullOk: false},
            trackId:      {type: ID, nullOk: false},
        },

        navigationProperties: {
            room:     'Room',
            speaker:  'Person',
            timeSlot: 'TimeSlot',
            track:    'Track'
        }

    });
}

function addRoom() {
    addType({
        name: 'Room',
        dataProperties: {
            id:   {type: ID },
            name: {max: 50, nullOk: false }
        }
    });
}

function addTimeSlot() {
    addType({
        name: 'TimeSlot',
        dataProperties: {
            id:            {type: ID },
            start:         {type: DATE, nullOk: false },
            isSessionSlot: {type: BOOL, nullOk: false },
            duration:      {type: ID,   nullOk: false }
        }
    });
}

function addTrack() {
    addType({
        name: 'Track',
        dataProperties: {
            id:   {type: ID },
            name: {max: 50, nullOk: false }
        }
    });
}

Boiler plate

Of course we haven't told you everything yet. there is some boiler plate necessary to set this up. The addTrack method didn't pop out of thin air. In fact it is a convenience wrapper around one of the Metadata-Helper methods. It takes about 35 lines of boiler plate before you get down to the business of defining your model metadata.

Here's that boiler plate:

// 'Identity' is the default key generation strategy for this app
var keyGen = breeze.AutoGeneratedKeyType.Identity;
// namespace of the corresponding classes on the server
var namespace = 'CC.Model'; 

// Breeze Labs: breeze.metadata.helper.js
// https://github.com/IdeaBlade/Breeze/blob/master/Breeze.Client/Scripts/Labs/breeze.metadata-helper.js
// The helper reduces data entry by applying common conventions
// and converting common abbreviations (e.g., 'type' -> 'dataType')
var helper = new breeze.config.MetadataHelper(namespace, keyGen);

/*** Convenience fns and vars ***/
var store; // the metadataStore that we'll be building

// addType - make it easy to add the type to the store using the helper
var addType = function (type) { helper.addTypeToStore(store, type); };

// DataTypes
var DT = breeze.DataType;
var BOOL = DT.Boolean;
var DATE = DT.DateTime;
var ID = DT.Int32;

This topic is incomplete. We'll flesh it out shortly. Meanwhile, we hope you get the sense that writing your own metadata in JavaScript is fairly obvious and not overly tedious.