Metadata
Breeze relies on metadata about entity types and their relationships when it queries, saves, and creates new entities.
This page is evolving and is not ready for publication.
It will cover:
-
The MetadataStore, a container of EntityType information
-
The EntityType details,
- how it creates new entities
- data properties
- key properties
- navigation properties
- default property values
- validation rules
- concurrency mode
- unmapped properties
-
Getting metadata from the persistence service with the EntityManager’s fetchMetadata
-
Translate property names between client and server formats with a NamingConvention
-
The default shared instance of the MetadataStore vs. private stores.
Please consult the API documentation for the following related classes:
MetadataStore
EntityType
DataProperty
DataType
NamingConvention
NavigationProperty
AutoGeneratedKeyType
NamingConvention
NamingConvention
By default, the property names of EntityType on the client are the same as the property names of the corresponding types on the server. If it is called "Person.FirstName" on the server, it will be "Person.FirstName" on the client.
Many of us prefer camel case names in our JavaScript. We still want to keep "Person.FirstName" on the server (we may not have a choice). But we want "Person.firstName" on the client.
If that's what you want to do, call the following line of JavaScript early in your application before creating any MetadataStores or EntityManagers to establish the camelCase convention as the default for your application.
NamingConvention.camelCase.setAsDefault(); // a convention can self-register as the default
The rest of this section dives deeper into the subject.
Don't try to fixit with formatters
Let's mention what will not work before talking about what will work. The server-side JSON formatter is often capable of translating property names from server to client to server. It could translate between Pascal case ("FirstName") and camel case ("firstName") for you. The JSON.NET formatter used by ASP.NET Web API has this feature.
Don't use it! You'll confuse BreezeJS. Yes Breeze uses property names for accessing entity data. But it also uses them to compose the URL when you query for data on the server. URLs are officially case sensitive and so are the property names of classes on the server. When you look for a person whose first name is "Joe", Breeze must know if the query expression should be "FirstName eq 'Joe'" or "firstName eq 'Joe'". Breeze needs the server-side property name to compose the queries correctly. Disguising the problem with server-side formatter configuration will confuse Breeze. Again, don't do it.
Use NamingConvention
Use the Breeze NamingConvention feature instead. In essence, a NamingConvention is a pair of JavaScript translation functions that execute on the client: one to translate server to client names and one to translate from client to server names. A Breeze MetadataStore has a NamingConvention instance which it uses for translation (its namingConvention property tells you what convention that is).
There are two built-in conventions, both static properties of the NamingConvention class: (1) none which preserves the names in both directions and (2) camelCase which performs the "FirstName" / "firstName" translation you are looking for.
The easiest way to establish your preferred convention globally in your application is to set the NamingConvention default. Breeze creates new MetadataStores with this default unless you configure it to use a different convention.
Call the following line before creating any MetadataStores or EntityManagers to establish the camelCase convention as the default for your application.
NamingConvention.camelCase.setAsDefault(); // a convention can self-register as the default
The NamingConvention.defaultInstance property reports the current default convention. The none convention is the default ... by default.
You don't have to depend on the default convention. You can create a MetadataStore with a specific, alternative convention and then create an EntityManager that uses that MetadataStore:
var store = new MetadataStore({ namingConvention: NamingConvention.camelCase });
var manager= new EntityManager( { metadataStore: store });
Create a custom NamingConvention
You can create your own conventions too such as this one for removing underscores from server-side property names.
// Creates a convention that removes underscores from server property names
// Remembers them in a private dictionary so it can restore them
// when going from client to server
// Warning: use only with metadata loaded directly from server
var createNoUnderscoreConvention = function () {
var _underscoredNames = {};
var serverPropertyNameToClient = function (serverPropertyName) {
if (serverPropertyName.indexOf('_') != -1) {
clientName = serverPropertyName.replace(/_/g, ''); // remove all _
// remember this substitution
_underscoredNames[clientName] = serverPropertyName;
return clientName;
}
return serverPropertyName
}
var clientPropertyNameToServer = function (clientPropertyName) {
var serverName = _underscoredNames[clientPropertyName];
return serverName || clientPropertyName;
}
return new NamingConvention({
name: "noUnderscore",
serverPropertyNameToClient: serverPropertyNameToClient,
clientPropertyNameToServer: clientPropertyNameToServer
});
}
var noUnderscoreConvention = createNoUnderscoreConvention();
Now make it the default for your application:
noUnderscoreConvention.setAsDefault(); // a convention can self-register as the default
Sometimes your convention depends upon more than just the name of the property. For example, you might have special names for boolean properties. Breeze passes information about the property in the second parameter of the translation methods.
The following convention performs camelCase translation AND prefixes boolean properties with "is". Thus "VendingMachine.Enabled" on the server becomes "VendingMachine.isEnabled" on the client:
var booleanNamingConvention = new breeze.NamingConvention({
serverPropertyNameToClient: function (serverPropertyName, prop) {
if (prop && prop.isDataProperty && prop.dataType === DataType.Boolean) {
return "is" + serverPropertyName;
} else {
return serverPropertyName.substr(0, 1).toLowerCase() + serverPropertyName.substr(1);
}
},
clientPropertyNameToServer: function (clientPropertyName, prop) {
if (prop && prop.isDataProperty && prop.dataType === DataType.Boolean) {
return clientPropertyName.substr(2);
} else {
return clientPropertyName.substr(0, 1).toUpperCase() + clientPropertyName.substr(1);
}
}
});
booleanNamingConvention.setAsDefault();
