NamingConvention

By default, the property names of an EntityTypeon the client are the same as the property names of the corresponding type on the server. If a Person property is called FirstName on the server, it will be FirstName on the client.

Many of us prefer camel case names in our JavaScript. We want to refer to the firstName on the client even if we retain FirstName as the property name on the server (we may not have a choice).

Welcome to the breeze NamingConvention, the component responsible for translating between the server-side property name and the client-side property name. The name of the default none convention is “noChange”:

Don’t fix it on the server

Let’s mention what will not work before talking about what will work. A server-side serializer may be able to map property names on the server to property names on the client. For example, it could translate the PascalCase “FirstName” property name to the camelCase “firstName” property as it ships Person data to the client. The JSON.NET formatter used by ASP.NET Web API can be configured to do this.

Don’t do it! You’ll only confuse both BreezeJS and your server.

Yes, Breeze uses property names for accessing entity data. But it also uses them to compose the query URL and to construct the data for a save request.

When you search for a person whose first name is “Joe”, Breeze must know if the wire format should say FirstNameeq'Joe' or firstNameeq'Joe'. If you change the person’s name to “Joseph”, Breeze must know if the JSON request should be {"firstName":"Joseph"} or {"FirstName":"Joseph"}. Breeze needs the server-side property name to compose these requests correctly. Disguising the problem with a server-side formatter deprives Breeze of the information it needs to communicate correctly. Again, don’t do it.

Use the 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.

There are two built-in NamingConvention instances, both static properties of the NamingConvention class:

There is always a default NamingConvention instance. You can ask the NamingConvention for the current default.

breeze.NamingConvention.instance.name;// 'noChange'

The initial default is none which does nothing; it simply passes property names “as is” in both directions. Its name is ‘noChange’.

You can change the default to the preferred convention for your application.

Call the following line before creating any MetadataStores or EntityManagers to establish the camelCase convention as the default for your application.

// a convention can self-register as the defaultbreeze.NamingConvention.camelCase.setAsDefault();breeze.NamingConvention.instance.name;// now it's 'camelCase'

The MetadataStore and the NamingConvention

A Breeze MetadataStore requires a NamingConvention to handle property name translation.

When you create a new instance of a MetadataStore (explicitly or indirectly when you create a new EntityManager), you’re also pinning it to a specific NamingConvention instance. It will be pinned to the default convention unless you say otherwise:

Create a custom NamingConvention

You can create your own conventions. Here is one that translates between server property names with underscore (‘_’) word separators and camelCase property names on the client.

It assumes that every server-side property name is completely lowercase with ‘_’ separators, e.g. “first_name”. This convention will not properly round-trip a server property such as “First_Name” or “Can_of_Worms”!

getting info about the property

Sometimes your convention depends upon more than just the name of the property.

For example, you might only want to translate property names for certain EntityTypes. Or maybe you should only translate a name for a property that returns a particular DataType.

Fortunately, Breeze passes in the property definition as the second parameter to both translation methods … if it has a property definition.

The property definition will be missing when translating the name of a property of an anonymous type. The data returned from a projection query are the most common anonymous type objects.

You should always be prepared for a null or deficient property definition argument.

From the property definition you can learn a lot about the property whose name you’re translating.

serverPropertyNameToClient:function(propertyName,propDef){propDef.entityType.name;// the full name with namespacepropDef.entityType.shortName;// the name without namespacepropDef.dataType;// what type it returnspropDef.isDataProperty;// is it a Data prop or Nav prop?}

See the DataProperty API for details of likely property definition members. Remember that this parameter may be null or deficient so always be ready for missing information.

The following convention performs BOTH a camelCase translation AND prefixes Boolean properties with the word particle, ‘is’. If your VendingMachine server-side class has a Boolean property called ‘Enabled’, this convention turns it into ‘VendingMachine.isEnabled’ on the client:

functionBooleanNamingConvention(){varBOOL=breeze.DataType.Boolean;varcamelCase=breeze.NamingConvention.camelCase;returnnewbreeze.NamingConvention({name:'booleanNamingConvention',clientPropertyNameToServer:clientPropertyNameToServer,serverPropertyNameToClient:serverPropertyNameToClient});functionclientPropertyNameToServer(name,propDef){// guard against empty or deficient property definitionif(propDef&&propDef.isDataProperty&&propDef.dataType===BOOL){returnname.substr(2);// strip off the "is"}else{returncamelCase.clientPropertyNameToServer(name);}}functionserverPropertyNameToClient(name,propDef){if(propDef&&propDef.isDataProperty&&propDef.dataType===BOOL){return'is'+name;}else{returncamelCase.serverPropertyNameToClient(name);}}}varconvention=newBooleanNamingConvention();

Stateful NamingConventions

Sometimes there is no way to calculate a property name that safely round-trips.

It is essential that your convention translate reliably in both directions.

There are plenty of real world examples where you just can’t calculate both directions. As we noted earlier, for example, the custom UnderscoreCamelCaseConvention can’t handle a server property name with some upper case letters in it. A server property like “Can_of_Worms” will become “CanOf_Worms” on the client. You need a way to account for deviant cases like this one.

More often the problem is that you have a few unpredictable translations. For example, you are generally happy with the “camelCase” convention but you have a handful of property names that require special handling:

EntityType

Client

Server

Customer

customerName

CompanyName

Customer

zip

PostalCode

Order

freightCost

Freight

Person

firstName

FnName

A common solution to this problem is to create a dictionary with specialized client-to-server-name mappings. Your convention should try this dictionary first. If there is no mapping, it should fall back to your default convention.

Notice the mapping for the undefined type. This option affords support for anonymous type name translation.

But what if there are just too many names to remember in a dictionary?

Maybe you can calculate the client name from the server name but the process is not reversible. In that case, you could record the server property names in the convention instance itself as you translate them to client property names.

You’ll also leverage the fact that Breeze runs the NamingConvention server-to-client translation when you load the metadata.

Here is an example of a convention that follows that plan:

// Removes underscores from server property names// Remembers them in a private dictionary so it can restore them// when translating from client name to server name// Warning: use only with metadata loaded directly from serverfunctionNoUnderscoreConvention(){var_underscoredNames={};// hash of every translated server namereturnnewNamingConvention({name:'noUnderscore',clientPropertyNameToServer:clientPropertyNameToServer,serverPropertyNameToClient:serverPropertyNameToClient});functionclientPropertyNameToServer(clientPropertyName){varserverName=_underscoredNames[clientPropertyName];returnserverName||clientPropertyName;}functionserverPropertyNameToClient(serverPropertyName){if(serverPropertyName.indexOf('_')>-1){varclientName=serverPropertyName.replace(/_/g,'');// remove all _// remember this substitution_underscoredNames[clientName]=serverPropertyName;returnclientName;}returnserverPropertyName;}}

No NamingConvention for EntityType names

In fact, at this time the developer would find it extremely difficult to translate EntityType names in Breeze. If you need this feature, please vote for it on <a href="http://breezejs.uservoice.com/forums/173093-1-breezejs-feature-suggestions/suggestions/7079377-namingconvention-for-entitytype-names" "Suggestion on UserVoice" target="_blank">UserVoice</a>.

Beware of the baked-in NamingConvention

Each MetadataStore has a NamingConvention at birth.

varstore1=newbreeze.MetadataStore();// born with the then-current NamingConvention.defaultInstancevarstore2=newbreeze.MetadataStore({namingConvention:NamingConvention.camelCase});// born with the camelCase convention

Once a MetadataStore has been created, its NamingConvention cannot be changed directly.

If the MetadataStore is empty, it can be changed indirectly by importing metadata:

This can be an unwelcome surprise. Imagine you defined your MetadataStore with one convention. Then you imported metadata you need from some source and discover (after your app misbehaves mysteriously) that your store is locked into a different convention.

Suppose you want the metadata exported from an existing store but you don’t want the convention buried in that metadata. With a bit of cleverness, you can remove that “commitment” - make the metadata “naming convention agnostic” - and import that metadata into a new MetadataStore that has a different convention.

varx=JSON.parse(exportedMetadata);// from the previous exampledeletex.namingConvention;// delete the 'NamingConvention' nodecleanCopy=JSON.stringify(x);// serialize it again but without the 'NamingConvention' node// now load this NamingConvention-agnostic metadata into a new MetadataStorevarstore4=newbreeze.MetadataStore();// created with the default "noChange" conventionstore.importMetadata(cleanCopy);store.metadataStore.NamingConvention.name;// "noChange"

Key Points

Changing the default NamingConvention after you’ve defined a MetadataStore has no effect on that store.

When you import metadata to store ‘x’, the NamingConvention of the imported metadata trump ‘x’s current convention.

You can “purify” exported metadata of its embedded NamingConvention and then import it into another MetadataStore governed by a completely different convention.

Beyond the NamingConvention … the JsonResultsAdapter

The NamingConvention is ideal for property name translation. When you need to do more complex manipulations of data arriving from the server, you can turn to the JsonResultsAdapter