I have some logic whereby a graph of POCO entities need to be cloned and to do this I have created partial classes for each T4 generated self tracked entity and implemented ICloneable on each. Each entity can clone itself and iterates through all of its children in navigation properites and calls Clone() on each child entity, then finally returns a cloned instance of itself with its cloned children added. So it is a deep clone and works fine. In the cloned tree all Entities are in the Added state, which makes sense...as the cloned graph is all new entities.
The clone process ocours service-side within an entity context, though no data access is done by the clone process itself, the clone is performed on a newly retrieved a graph from the database (If there is a better way to clone an entity graph feel free to inform me...).
Now the cloned graph is then shipped to our Silverlight 4 application via WCF and is displayed there. The user can instantly save or add / modify data in this graph and then save it back to the service no problems, if however, they remove any nodes from the graph it is a problem.
When the user clicks a control to remove a node, I alter the entity graph by calling MarkAsDeleted(). This works fine for Unchanged graphs, but with my cloned graph with everything in the Added state, upon saving back to the database in the WCF service I am getting an OptimisicConcurrencyException.
"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
In the service I am calling
context.EntitySetX.ApplyChanges(entityNodeInTree);
context.SaveChanges();
This is all fine except in this one scenario. The work around for the user so far is to save the cloned graph back to the database, retrieve it again (now will all be Unchanged) and then delete any data they wish to and finally save again.
I do not understand why this is happening. Is it the case that you cannot MarkAsDeleted something that is in the added state? That doesnt really make sense to me as there are instances where items are both added and then removed from the graph during the use of our Silverlight app and this does not cause issues.
Any ideas?
Thanks
Solved!
The problem I was having was caused by new Added entities being added to one another during the server side clone where tracking was not turned on, but deletions, which happened client side, where tracking is always on (due to the deserialise turning tracking on) were being tracked.
The key is to turn tracking on during the clone of each Entity in the graph on the server side. So, when I 'new' an Entity, I first turn tracking on for the entity with the StartTracking method before doing anything else. This way, when I add cloned child entities to the cloned parent entity, it is already in tracking mode.
In short, before doing anything to self tracked entities, make sure all the entities in your graph have tracking turned on!
Related
Hi fellow iOS developers, I have seem to run into a roadblock here with coredata and Im fairly certain this is should be a common use case. But I am either not querying google correctly or my design with handling managed objects was bad which led to this problem.
So basically, the problem that I am facing is the following..
User creates a new managed object object.
User updates said new managed object object (say edit the name
field)
** The following is the problem **
User wants to edit the same field again, but decides to discard their changes
What is the right thing to do here? Initially I was using .refresh() on the managed object context, but that only works if the object is persisted in the database.
Essentially, I don't want to save the object until the user explicitly opts to "save". Which leaves me in this limbo land. At first one of my thoughts was to created regular objects and convert them into core data objects when "save" is selected. However, that proved to be inefficient especially when handling multiple relationships.
I would think that there is something that is provided that aims to solve this problem, im just not quite querying google correctly.
I want to ask for a safe way to clear subEntities in coredata.
I have my a many-to-many relationship like this: Product *<->* Product. Therefore, I've got to create a subEntity to hold some special values between (sortPosition, groupName.....).
So it's like this: Product *<->1 ProductSubEntity 1<->*Product.
When I download products from server's API, the easiest way to update correctly correspond to the server's result is:
Remove all child relationship ([self removeProductSubEntities:self.subEntities]).
Add sub from server's result.
Result: There'd be a lot of subEntity in coredata (which won't hold relationship to any product), and this might take storage/memory/cpu when CRUD (I think?). But I can't actual delete the subEntity (in case it's being hold reference to as an viewController's Object somewhere, and it might cause crash: access to a deleted object).
QUESTION:
How can I clear those sub entities (might occur sometimes) if:
No relationship to any product.
No actual reference from anywhere (any viewControllers or objects)???
P/S: I'm thinking of implement a batch delete when terminate app. Could that be consider a safe solution?
I don't consider this to be a datastore issue, rather a UI update issue. You should delete the objects from the datastore when you don't need them any more and you should update the UI accordingly.
1 thing you didn't mention is re-use. It's possible that your download may be an update to an existing item, which you could find and update, then life is easy all round. Arguably everything below still applies in this case though as your UI might not update to reflect changes and you may need to refresh the managed object.
For the UI update it's generally wise to observe the datastore for changes, usually with an NSFetchedResultsController. If you're doing this then your UI would automatically update itself with the changes.
If you're explicitly passing entity instances around then you should have some way to trigger an update explicitly, and exactly how that works depends on your UI. Generally speaking you'd be doing something like posting a UINotification to tell the system that the datastore changed and they need to re-validate their data objects. For the UI you shouldn't be showing now-dead objects to the user, and in your question where you talk about not deleting to avoid crashes, it's probably worse to allow the user to update invalid objects and just quietly not telling them that their updates won't be saved. When the notification is received you may want to pop a (some) controller(s) off the stack, or re-query the datastore for the new data to be displayed.
If for some reason you don't want to do the above, then yes, you can query for all of the entities with a nil relationship and then batch delete them. This should be done on a background thread just like data loading and I'd recommend doing it on app load instead of close (because you won't have so many view controllers loaded and the ones that are should all have only valid references now...).
I want to remove all entities which are not represented on the server anymore (e.g when another client deletes it).
My current approach is to delete all entities from a specific type which are not returned after a query anymore:
Get and cache all local entities from specific type
Make the query
Compare old entities with new entities which are returned from the query and detach it from entity manager
This seems a little work for me and i wonder if there not an easier way (maybe breeze already has something) to do that?
It's a good question. We refer to these types of entities as "ghosts". They are entities that are queried from a server by one user and later deleted by another user. The entities on the first client have now become 'ghosts'.
We have an existing feature request that would automatically 'detach' these if and only if a primary key requery occurs. The reason for this is that unless the entity was fetched by primary key we cannot determine that it is a ghost. We have not yet implemented this feature but are considering it for one of the near term releases. Would this feature be of use to you?
I am using BeforeSaveEntity event of the to initialize coordinates (latitude, longitude) information from the third party service.
I convert the EntityInfo.Entity in required destination type and I update the value in it. However when it goes to saveChanges() method it does not push my updated values to the database.
I assume somehow the context cannot track the modification I made to the entity.
Can somebody please help?
Just a guess here but in Breeze v 1.1.3 we added a the EntityInfo.ForceUpdate boolean property but it never made it into the main Breeze documentation, it only appeared in the release notes.
This property may be used to force a server side update of an entire entity when server side modification has been made to any property of an existing entity. The other approach that may be used is to explicitly update the EntityInfo.OriginalValuesMap.
The idea behind both of these is that on an update Breeze only creates an update statement for those se properties that have been changed. Any client side changes are automatically detected because of Breeze's tracking mechanism which adds an entry into an 'originalValuesMap', but this cannot be done automatically for server side changes because the server side entities are not instrumented to perform notification about property changes.
i would like to set an entity sent from the server to "added". it looks like entityaspect has methods setdeleted, setmodified, etc... but i can't seem to find one called setadded... what is the cleanest way to set an entity to "added"? i was thinking perhaps i would need to detach and then attach as "added". i have a server method called "newdeal" which creates a new entity ready for data entry... this method has business logic which i would prefer to keep on the server... when it gets to the client the entity is marked as "unmodified" which makes sense... but i would then like to change it to "added"...
thank you
#giancarloa, I'm assuming that, by the time the entity is sent from server to client, it has been persisted in the database. If that's the case, it wouldn't make sense to have its entityState set to Added as it would cause a duplicate error. If that's not how it works, please explain in detail what you are doing as I'm trying to get an idea of all the steps you're taking.
I'm also confused as to why create an entity in the server, send it to the client, update it, and then send it back to the server to save it in the DB - this just appear to cause more traffic and possibly reduce performance. Also, what it the user decides not to save? - then the work in the server would've been wasted.
Why not create the entity in the client and if it turns out to be saved, then the business logic would kick in the server during the beforeSaveEntity/beforeSaveEntities?
I had a similar problem. The breeze expect that entities returned from the server already exists in your database. This is not the case if your server fetched the entities from some other sources (not the database), returned them to client and then user can decide to the client if those entities should really be inserted in the database.
As you indicated, what you must do is skip the code that adds the entities into client's entity manager. later, you can add the detached entities to Entitymanager.
See the following answer for more details.
https://stackoverflow.com/a/18596070/174638