I am aware that it is a weird approach but I have to create and save a 'parent' entity before I can add children. The problem I am having now is that if the user decides to stop creating the new children the parent is still created.
So my question is: Is it possible to undo the last save in Breeze? Another option is deleting the saved items based on their IDs from the result.entities that's generated after the save but that would be very cumbersome.
(If its any help, I'm using AngularJS)
Related
I have a grid which shows a list of entities.
Each row has a delete button.
Once the user clicked on the delete button for a given entity I want to change the css of the row and to replace the delete button with a cancel button.
So on the delete button event handler, I do :
myEntity.entityAspect.setDeleted();
But as soon as I do that, the entity is removed from the collection and the row disappear from the grid.
Is there a way to prevent that ? I just want to mark the entity as 'deleted', and postpone any change until user clicks on save button.
The only alternative I see, is to add a property isDeleted to my client-side model and to base my logic on that. But it means I have to handle change tracking myself and loop through the entities on save to call setDeleted for entities which isDeleted property is true. I'm not fond of this solution. Is there something better I'm missing with breeze ?
My guess is that your collection is the value of one of Breeze's navigation properties. These collections are 'live' in that if you delete an entity, the entity becomes removed from all live collections to which it belongs.
So the best answer would be to copy the entities into your own collection and bind to that. Your own collection will not be 'live' and deleting entities will not remove them from the collection.
The problem with 1) is that when you delete an entity we need to sever its relationships with any other entities. This really is the essence of deleting, we want the environment to look like it will after the delete is committed ( via EntityManager.saveChanges())
When you say that you do not want the navigation properties to be set to null after a delete I am assuming that you are talking about a scalar (1->1) or (n->1) navigation. (nonscalar navigations simply remove the entity from the collection) The first issue is whether you are talking about navigations 'To' the deleted entity or 'From' the deleted entity. In the case of 'NOT" removing the navigation 'to' a deleted entity we would be causing many if not most existing breeze apps to fail. Most apps expect to see entities disappear when you delete them.
In the case of 'NOT' removing the navigation 'from' a deleted entity, the concept make more sense except that you cause two more issues. The first is that this breaks any 'bidirectional' navigations, i.e. you can no longer roundtrip between the 'from' and 'to' navigations. But more importantly, we view a 'delete' as a precursor to an eventual 'detach' operation ( which will occur after a successful save). The primary value of the 'detach' is that it recovers memory because we have effectively removed any references to the 'deleted' object, which means that it can be garbage collected. If we leave any navigation properties intact this garbage collection will never occur, and we end up with a memory leak.
We could get around this by having the 'detach' operation also remove navigation properties but the rules begin to get harder to explain.
If you still feel strongly about this, please post your suggestion to the Breeze User Voice. If we see some substantial interest in this, we will try to come up with a solution, but right now we don't have a good answer to this that doesn't add real conceptual complexity. (Something we really try to avoid).
Can you give more detail about the 'validations' that you are seeing? Are these validations on the 'deleted' entity or on the still alive entities that were pointing to the deleted entity? Not having the first occur makes a lot of sense (and we should fix it if this is the case), not having the second occur does not make sense because you really are causing a real validation failure.
Ok, I have tried Jay's solution. First I do a deep copy using lodash.js:
$scope.sourceMaterials = _.clone($scope.request.sourceMaterials, true);
then I bind my grid to $scope.sourceMaterials. User clicks on delete button for a given row and I do:
var document = $scope.sourceMaterials[index];
document.entityAspect.setDeleted();
The problem is that breeze sets to null all the navigation properties of my entity (see removeFromRelationsCore function in breeze.js).
Doing so affects the values shown on screen to the user. For instance one of the required field gets nulled by breeze. So when user clicks on the save button, the validation fails.
To sum up I have two issues:
navigation properties should not be set to null when deleting my entity
the validation should not be triggered for an entity that has its state set to deleted
Could you shed some light on this please ?
EDIT
Following Jay's answer, I can confirm that:
1) navigation property is nulled FROM the deleted entity and I'd like that to be prevented somehow
ex: I have an entity Document with a 1 -> 1 np named DocumentType. If I set deleted on Document, then DocumentType is nulled.
2) validation rules occur on properties of the deleted Entity (those that were nulled)
which one of these problems should I report on user voice ?
I am trying to make an application where a user can edit attributes of a managedObject in a view, and select either Done to persist the changes, or Cancel to undo the changes.
To implement this behavior, I plan to do the following-
When the view controller is loaded to edit the managedObject, create a backupManagedObject which is a clone of the original managedObject.
This backupManagedObject is created in a separate child ManagedObjectContext.
Let the user edit the original managedObject.
If:
Done is pressed, the original managedObject is persisted, and backup is deleted
Cancel is pressed, the backupManagedObject is cloned into original managedObject and the backup is deleted
My question is, once I am done with the backupManagedObject, how can I delete the childManagedObjectContext which will have no more managed objects and I don't plan to use them anymore (for every new view controller, I plan to just create a new child managed object context and destroy it once view controller is done).
You should do this the other way around:
When you load your editing View Controller, create new Managed Object Context that is the child of your main one, let's call it the editingMOC.
Do the edits to the editingMOC, and if you want to persist them, save the editingMOC (this will propagate changes to the mainMOC), and then save the mainMOC to save the data to the persistent store.
If you wish to discard the changes done to the editingMOC, simply do not save them and let the context get dealloc'd.
In general, a managed object context is release the same way any other object in Objective-C is released and deallocated.
If you are using ARC, simply set the property to nil when you no longer need it and it will be destroyed along with any unsaved changes.
However, your approach for this problem is a bit complicated.
You could simply create a new "editing" child context, fetch the objects you like to edit in that context and make changes to the objects.
If the user decide to commit the changes, save the context (up to the store), and if not, simply destroy the context.
An easier way is to simply create the view and populate the UI controls (text fields, etc.) using data from the NSManagedObject attributes. If the user makes any edits then set a flag so you know if changes are made and then when they select Done update the NSManagedObject attributes using values from the UI controls and save the MOC. If they select Cancel then don't do anything.
See this link for a video showing an app using a similar approach for editing Core Data records on iOS. The OSX app uses standard NSPersistentDocument undo manager.
http://ossh.com.au/design-and-technology/software-development/uimanageddocument-icloud-integration/os-x-and-ios-app-integration-with-core-data-and-icloud/
I found a question very similar to mine here, but it was un-replied to and unanswered so I will try again.
I may be missing something in regards to cascade deletes and core data. I am used to cascade deletes working as designed in RDBMS apps but having issues with core data.
I have an object record that get's inserted into entity via an "add form" called modally from a table view. No problem.
In another session I will insert objects into a related details entity (many) where there is a common loadID attribute in both. No problem.
In another session, I will call up the original table view to view the parent "loads", and swipe-delete, save the context, and the parent load gets deleted from the one side entity. No problem, (except apparently details objects in a many side entity do not get removed)
When I open the sqlite data source with a db manager, I see the child records (pay detail items) are being orphaned. I have double and triple checked the cascade delete settings in the relationships. Have tried different entity relationship settings combinations, but nothing seems to get the many records to automatically delete along with the parent record.
If you can't define corresponding keys in core data, how does core data know what belongs to what when you operate in multiple sessions (contexts) adding child objects to the many-side entity?
I don't know if I'm doing something wrong or missing a vital step prior to inserting new child objects in the many table, or doing something wrong or missing a step when deleting the parent object.
But I would suggest all with cascade deletes set to open your data file and be sure there are not orphaned objects (records)
I have a program in which I'm using CoreData to manage both permanent and temporary data.
In one instance, I abort the addition of some data by deleting the object when the user presses cancel. But when I hit the next view, the data is still there, attached to it's parent core data object.
[self.bar removeFoosObject:self.foo];//Why do I need this line?
[self.foo.managedObjectContext deleteObject:self.foo];
I eventually solved this by manually removing the child object from it's parent -- but isn't that something core data handles automatically? Why should I need the first line?
I ran some test code, and Foo is definitely deleted -- the code that it was mucking up let me check, and it's MOC has been set to nil. The memory exists, but it should be very, very dead...
You have to manually do this because you have your deletion rules set wrong. Check the Relationship Delete Rules in the following apple documentation.
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html
Or it could also be that the relationship is NOT set as optional between the parent and child object.
And also after you delete the object you should save the database to synchronize it.
I'm using Entity Framework with an AS.NET MVC application. I need to allow the user to create new records and modify existing ones. I am able to fetch existing records no problem, but when I pass back in the edited entity and try to save it it creates a new one and saves it and leaves the original unmodified.
I am getting the object from EF using the primary key (e.g. ID number for an employee record). I successfully retrieve it, and set the MergeOption like so:
Context.Sector.MergeOption = MergeOption.NoTracking;
I am able to trace that the object has the correct data (using the key of the original record) all the way down to the point where I call:
Context.SaveChanges();
However, after that, the new record is created instead of modifying the existing one.
Is there something obvious I am missing here? I would have thought that retrieving the object and changing some of its values (not the ID) and saving it would just work, but obviously not.
Thanks,
Chris
"NoTracking means that the ObjectStateManager is bypassed and therefore every access to the Entity Objects results in a fetch from the database and the creation of new objects."
-- http://blog.dynatrace.com/2009/03/11/adonet-entity-framework-unexpected-behaviour-with-mergeoptions/
I don't think NoTracking is what you want.
From your comment: "distributed across various tiers and some proprietary libraries"
Are you new()ing up a ObjectContext, closing it or losing the reference to it, and then trying to save your object to a new() or different ObjectContext?
If so your losing all of your change tracking information. If this is the case then you want to call the Attach() method to reattach the entity to the context, ApplyPropertyChanges() and then finally SaveChanges().
Julie Lerman has a pretty good blog post that outlines all the different change tracking options and techniques that are available. You should also check out this MSDN article on the same subject.