In my spa I need to send a complex object tree to the client and let the user fill in his data, before the entities can be saved in the database. So the entities do not exist in the database at that moment. The object tree is constructed in memory on the server and simply fetched by a call to breeze.EntityQuery.from("ComplexeObjectTree")
. The entities a bound via knockout to a web form. The user than enters the required data first, before he can push the save button. Some of the entities properties in the form are optional and have default values. If the user doesn’t change these properties, beeze will not detect any changes and will not put its owning entities into the change set. They are marked unchanged but their id values are still -1
(not saved yet). But the database save can only be executed successfully, when all entities in the object tree including their relations have been send to the server (to respect database constrains). The save operation should result in one or multiple insert statements, also for those entities that have not changed on the client, but are new to the database (id==-1
). How can I handle this use case? Shouldn’t all entities with primary key id==-1
marked as Added and be part of the save change set by default?
Any help is very much appreciated,
Andreas
Let's do it your way and accept your claim that the object graph must be constructed on the server. If I understand properly, you are offering the user an opportunity to tweak some of the values you generate on the server before committing them to permanent storage. Sounds like a fine plan.
"ComplexObjectTree" does NOT have to be an entity graph. It can be an arbitrary data object. It could still be a CLR type but it wouldn't be (or contain) objects described in metadata nor would they be known to EF or to the Db. This object graph exists simply as info that drives construction of the real entity graph on the client. It has all the hard-to-calculate values in it that you don't want calculated on the client ... like the answer to the meaning of life and who wins the Super Bowl this year.
Your query returns this object with data but no entities in it. The querySuccess callback, CreateComplexTree(data)
, will make the entity graph on the client out of this data.
The entity graph will be composed of entities understood by the Breeze client, thanks to metadata. Unless you have an objection, these entities are also understood on the server and are ready to save and query like any other entities (you can work around that assumption if false ... but let's work on one problem at a time).
So you're all set. You walk your data graph, creating the entities as you go. When you see (-1) in the data, you know to create a new instance of the type. Copy the data into the entity initializing object which is the second argument in manager.createEntity('Foo', {...})
. There's no business logic here - no calculation - just the monkey-work of copying.
I can't tell if EVERY object within the graph is to be inserted or only some of them. Maybe some of them are pre-existing entities that are unchanged unless the user changes them too. No biggie. You can follow the same approach to construct "Unchanged" entities on the client with a line like manager.createEntity('Bar', {...}, breeze.EntityState.Unchanged)
.
Now you have a proper Breeze entity graph, ready for binding, navigating, and saving. If the user loves it just the way the server created it, she presses the [Save] button, Breeze finds all the changes in the entity graph, and off they go to the server.
As Bryant observes, you can't really trust the client. You'll want to walk the graph to validate it. But that's true regardless of your methodology.
Hope this helps.