4
votes

I'm having problems trying to save a new child object via breeze, my model boils down to this

Parent Foo has zero or more child objects of type bar ie Parent has a property of type ICollection called bars and the Bar object has a property of type Foo - its parent

I can fetch, update and insert new instances of "parent" Foo objects via Breeze - this is using the web api controller but I get a constraint violation when creating a new instance of Bar, setting its parent Foo (already in the cache) and calling save changes

I have code similar to this

var newBar = entityManager.createEntity("Bar");
newBar.Foo = existingFoo;
entityManager.saveChanges();

I've also tried adding the child to the parents collection eg existingFoo.bars.push(newBar);

and explicitly calling
entityManager.addEntity(newBar);

before calling save changes but the error is the same

Inspecting the JObject saveBundle passed to the controller I can see the new child bar object but I don't see any reference to the parent object

This is a simple one to many relationship which I think is supported? - I can see the relationship in the metadata which looks correct

The exception message looks like this

Entities in 'FooBarContext.Bars' participate in the 'Foo_Bars' relationship. 0 related 'Foo_Bar_Source' were found.

What am I doing wrong?

Update 1: Metadata generated by the controller:

"{\"schema\":{\"namespace\":\"BreezePlayground.Models\",\"alias\":\"Self\",\"d4p1:UseStrongSpatialTypes\":\"false\",\"xmlns:d4p1\":\"http://schemas.microsoft.com/ado/2009/02/edm/annotation\",\"xmlns\":\"http://schemas.microsoft.com/ado/2009/11/edm\",\"cSpaceOSpaceMapping\":\"[[\\"BreezePlayground.Models.Foo\\",\\"BreezePlayground.Models.Foo\\"],[\\"BreezePlayground.Models.Bar\\",\\"BreezePlayground.Models.Bar\\"]]\",\"entityType\":[{\"name\":\"Foo\",\"key\":{\"propertyRef\":{\"name\":\"Id\"}},\"property\":[{\"name\":\"Id\",\"type\":\"Edm.Int32\",\"nullable\":\"false\",\"d4p1:StoreGeneratedPattern\":\"Identity\"},{\"name\":\"Name\",\"type\":\"Edm.String\",\"fixedLength\":\"false\",\"maxLength\":\"Max\",\"unicode\":\"true\",\"nullable\":\"true\"}],\"navigationProperty\":{\"name\":\"Children\",\"relationship\":\"Self.Bar_Parent\",\"fromRole\":\"Bar_Parent_Target\",\"toRole\":\"Bar_Parent_Source\"}},{\"name\":\"Bar\",\"key\":{\"propertyRef\":{\"name\":\"Id\"}},\"property\":[{\"name\":\"Id\",\"type\":\"Edm.Int32\",\"nullable\":\"false\",\"d4p1:StoreGeneratedPattern\":\"Identity\"},{\"name\":\"Description\",\"type\":\"Edm.String\",\"fixedLength\":\"false\",\"maxLength\":\"Max\",\"unicode\":\"true\",\"nullable\":\"true\"}],\"navigationProperty\":{\"name\":\"Parent\",\"relationship\":\"Self.Bar_Parent\",\"fromRole\":\"Bar_Parent_Source\",\"toRole\":\"Bar_Parent_Target\"}}],\"association\":{\"name\":\"Bar_Parent\",\"end\":[{\"role\":\"Bar_Parent_Source\",\"type\":\"Edm.Self.Bar\",\"multiplicity\":\"*\"},{\"role\":\"Bar_Parent_Target\",\"type\":\"Edm.Self.Foo\",\"multiplicity\":\"0..1\"}]},\"entityContainer\":{\"name\":\"FooBarContext\",\"entitySet\":[{\"name\":\"Foos\",\"entityType\":\"Self.Foo\"},{\"name\":\"Bars\",\"entityType\":\"Self.Bar\"}],\"associationSet\":{\"name\":\"Bar_Parent\",\"association\":\"Self.Bar_Parent\",\"end\":[{\"role\":\"Bar_Parent_Source\",\"entitySet\":\"Bars\"},{\"role\":\"Bar_Parent_Target\",\"entitySet\":\"Foos\"}]}}}}"

Sample code:

 var managerInsert = new breeze.EntityManager("/api/FooBar");
    managerInsert.fetchMetadata().then(function () {
        managerInsert.fetchEntityByKey('Foo', 1, false)
        .then(function (data) {
            var child = managerInsert.createEntity('Bar');
            child.Parent(data.entity);
            child.Description("bar desc");
            managerInsert.saveChanges().then(function () {
                return true;
            })
                .fail(function () {
                    alert('save failed');
                });
        })
        .fail(function (data) {
            alert('fail');
        });
    });

generated request payload:

{"entities":[{"Id":-1,"Description":"bar desc","entityAspect":{"entityTypeName":"Bar:#BreezePlayground.Models","entityState":"Added","originalValuesMap":{},"autoGeneratedKey":{"propertyName":"Id","autoGeneratedKeyType":"Identity"}}}],"saveOptions":{"allowConcurrentSaves":false}}

The child object is inserted but the foreign key is not set

I'm assuming this is something to do with the ef mapping and generated metadata?

Update 2: Foreign Key Property Required?

I compared my code with the new Breeze SPA template ToDo app code which clearly works and the key difference I spotted in the generated metadata was that the ToDo app model exposes the parent object foreign key.

I added that to my simple Foo Bar model and was able to get the save of a child object to work - my understanding is that ef makes it optional to expose the foreign key in the model but it seems that is a requirement for Breeze in order to generate the correct metadata? - did I miss that in the docs somewhere?

1

1 Answers

4
votes

Good catch! We do need to do a better job of documenting this, and as a result of this post are in the process of updating the docs on this topic.

As you discovered, breeze assumes that you are using "Foreign Key Associations' instead of 'Independent Associations' ( in EF parlance).

The reason for this requirement, which can be bypassed but with a substantial loss of functionality, is that the existence of foreign keys on the client is what allows breeze to automatically fix up relationships between entities that might be queried separately.

See the following MSDN article for more background: foreign-keys-in-the-entity-framework