Skip to content

Creating entities ​

Breeze creates entities in two situations:

  • when it materializes query results, which happens without your involvement
  • when you ask it to create a new entity

This page is about the second.

EntityManager.createEntity ​

The usual way to make a new entity is createEntity on an EntityManager:

ts
const order = em.createEntity(Order, {
  shipName: 'Alfreds Futterkiste',
  orderDate: new Date(),
});

The first argument is the entity's registered class (Order), or the name of the entity type ('Order'). Don't confuse the name with the resource name ('Orders') you use in EntityQuery.from.

The second argument, the initializer, is optional. It is a plain object whose values are copied onto the new entity. It is usually the simplest way to create and initialize an entity in one step.

Passing the class also checks the initializer: every property must be one the class declares, and each value its declared type. That matters, because at runtime createEntity ignores a property it does not recognize, without a word — { ShipName: 'Acme' } in server casing, or a typo, creates the entity without the value. A collection navigation property takes a plain array of entities and a complex property a plain object of its own values, as they do at runtime. For values the compiler cannot see, pass a Record<string, any> or use the type name. The type is InitialValues.

createEntity does three things:

  1. builds the entity from metadata: every data, navigation and complex property is present
  2. adds it to the manager's cache, with an entityState of Added
  3. gives it a temporary key, if the type's key is generated by the server (see below)

The full signature is:

ts
createEntity(
  entityType: string | EntityType,
  initialValues?: object,
  entityState?: EntityState,    // default EntityState.Added
  mergeStrategy?: MergeStrategy // default MergeStrategy.Disallowed
): Entity

Choosing the entity state ​

Pass an EntityState as the third argument to create the entity in a different state:

ts
import { EntityState } from 'breeze-client';

// Not in the cache yet. Finish configuring it and add it later.
const draft = em.createEntity(Employee, { lastName: 'Smith' }, EntityState.Detached);

// In the cache as if it had been queried. You must supply the key.
const existing = em.createEntity(Employee, { employeeID: 42, lastName: 'Jones' },
  EntityState.Unchanged);

Only Added entities get a temporary key. If you create an entity as Unchanged or Modified, supply the real key in the initializer.

The fourth argument decides what happens when the cache already holds an entity with the same key. The default, MergeStrategy.Disallowed, throws.

Initializing navigation and complex properties ​

An initializer can set navigation properties, as well as data properties:

ts
const detail = em.createEntity(OrderDetail, {
  order: existingOrder,
  product: existingProduct,
  quantity: 5,
});

Setting order sets the detail's orderID foreign key and adds the detail to existingOrder.orderDetails. See Navigation properties.

A complex property can be initialized with a nested object:

ts
const supplier = em.createEntity(Supplier, {
  companyName: 'Exotic Liquids',
  location: { city: 'London', country: 'UK' },
});

See Complex properties.

Keys ​

Every entity in a cache must have a unique key. How the key is set depends on the entity type's autoGeneratedKeyType, which comes from metadata:

AutoGeneratedKeyTypeWho sets the key
NoneYou do, before the entity is added to the cache.
IdentityThe database. Breeze gives the entity a temporary key until it is saved.
KeyGeneratorA server-side key generator. Breeze gives the entity a temporary key until it is saved.

Temporary keys ​

When you add an entity whose key is store-generated and still has its default value, Breeze generates a temporary key:

ts
const order = em.createEntity(Order);
order.orderID;       // -1
order.entityAspect.hasTempKey;      // true

Temporary integer keys are negative (-1, -2, -3, …), so they can't collide with real database keys. Temporary Guid keys are new GUIDs.

The integer sequence is shared by every entity type and every manager in the app, so the first Order you create may get -3 if two other entities got temporary keys before it. That keeps temporary keys unique across types; don't rely on the particular number.

You can ask for a temporary key explicitly with em.generateTempKeyValue(entity), which sets the key and returns it. Temporary keys only work for single-part keys.

When you save, the server assigns the real key and returns a mapping from temporary to real values. Breeze updates the entity's key, and the foreign keys of every cached entity that pointed at it. SaveResult.keyMappings lists the mappings if you need them. See Saving changes.

Client-set keys ​

If the type's key is not generated, you set it. Northwind's OrderDetail has a composite key, orderID plus productID, which you must set before the detail goes into the cache:

ts
const detail = em.createEntity(OrderDetail, { orderID: 10248, productID: 11 });

If you leave the key unset, createEntity throws:

Cannot attach an object of type (OrderDetail:#...) to an EntityManager without first
setting its key or setting its entityType 'AutoGeneratedKeyType' property to something
other than 'None'

Setting the key through the related navigation properties (order, product) does the same job.

EntityType.createEntity ​

em.createEntity is shorthand for creating the entity from its EntityType and then adding it:

ts
const orderType = em.metadataStore.getAsEntityType('Order');

const order = orderType.createEntity({ shipName: 'Alfreds Futterkiste' });
// order.entityAspect.entityState === EntityState.Detached

em.addEntity(order);
// now Added, with a temporary key

You might write this if you create a lot of entities of one type, and want to look up the type once rather than on each call.

An entity created this way:

  • has every property that metadata defines for its type
  • already has an entityAspect, so it is a Breeze entity
  • is detached: it doesn't belong to any EntityManager until you add or attach it

A detached entity is only partly functional. Its navigation properties can't find related entities, because it isn't in a cache. Add it to a manager as soon as you can.

addEntity and attachEntity ​

MethodResulting stateTemporary key generated?
em.addEntity(entity)Addedyes, if needed
em.attachEntity(entity)Unchangedno
em.attachEntity(entity, state)stateonly when state is Added

Use attachEntity for an entity that already exists in the database — for example, one you built from data you got some other way.

An entity can belong to only one manager. Attaching it to a second manager throws; detach it from the first manager, or clear that manager, before you attach it.

Don't use new, usually ​

You can create an entity with new if you have written a constructor for the type and registered it with metadataStore.registerEntityTypeCtor. Breeze wires it up when you attach it. Most applications don't need to, because Breeze builds the type from metadata. If you write a constructor, it is usually to add behaviour rather than to list every property. See Extending entities.

Released under the MIT License.