How to assign fetchedObjects to a variable independently - swift

In my case i 'm getting my nsmanagedobjects into an array with line of
let rows = self.fetchedResultsController.fetchedObjects as [Row]
after this line i rollback changes as my needs
managedObjectContext?.rollback()
and i am supposed to use the values of the rows variable. But due to rollback() function changes in rows are also deleted.
How can i keep my data for after rollback() function call?

"But due to rollback() function changes in rows are also deleted."
That's because this is specifically what that method is documented to do. The documentation says:
Removes everything from the undo stack, discards all insertions and deletions, and restores updated objects to their last committed values.
The entire purpose of calling rollback is to get rid of any unsaved changes in the context, so what you describe is the expected behavior.
If you want to keep changes and call rollback for some reason, you would have to copy those changes to someplace not affected by the managed object context, then I guess copy them back later on. That will be a pain in the ass, but it's possible.
The real question is why you are calling rollback-- a method specifically designed to undo unsaved changes-- if you have changes you don't want to undo.

Related

Could I save Postgres transaction and continue work with db within it later

I know about prepared transaction in Postgres, but seems you can just commit or rollback it later. You cannot even view the transaction's db state before you've committed it. Is any way to save transaction for later use?
What I want to achieve actually is a preview (and correcting) of some changes in db (changes are imports from csv file, so user need to see preview before apply it). I want to make changes, add some changes later, see full state of db and apply it (certainly, commit transaction)
I cannot find a very good reference in docs, but I have a very strong feeling that the answer is: No, you cannot do that.
It would mean that when you "save" the transaction, the database would basically have to maintain all of its locks in place for an indefinite amount of time. Even if it was possible, it would mean horrible failure modes and trouble on all fronts.
For the pattern that you are describing, I would use two separate transactions. Import to a staging table and show that to user (or import to the main table but mark rows as "unapproved"). If user approves, in another transactions move or update these rows.
You can always end up in a situation where user can simply leave or crash without clicking "OK" or "Cancel". If what you're describing was possible, you would end up with a hung transaction holding all these resources. In my proposed solution you end up with wasteful rows in "staging" table that you may still show to user later or remove.
You may want to read up on persistence saga. This is actually a very simple example of a well known and researched problem.
To make the long story short, this pattern breaks down a long-running process like yours into smaller operations that are applied and persisted in some way in separate transactions. If any of them happens to fail (or does not occur as expected), you have compensating actions that usually undo what the steps executed so far have done (e.g. by throwing away stale/irrelevant data).
Here's a decent introduction:
https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/#:~:text=The%20SAGA%20Pattern,completion%20of%20the%20previous%20one.
http://vasters.com/clemensv/2012/09/01/Sagas.aspx
This concept was formally introduced in the 80s, but is well alive and relevant today.

How to modify transaction behavior in EF Code First

In Code-First Entity Framework, is there a way to modify the transaction behavior so that it does not discard all changes and instead keep all the changes up to the point of failure?
For example:
foreach {var objectToSave in ArrayOfEntityObjects)
{
MyContext.Insert(objectToSave);
}
try
{
MyContext.SaveChanges();
}
catch (Exception x)
{
//handling code
}
In the above example, assuming the array is an array of 100 objects and that an error occurred on item 50, I want to keep the ones that were successful, up to the point of failure at least. Right know we are executing the MyContext.SaveChanges() command during each iteration of the foreach loop to do this, but we would like the performance boost of committing to the database in one commit (Our understanding is that EF sends all the commands at once for the transaction, over a single connection, thus only using one round trip).
No, there is no way to do this automatically. EF commits all changes in a single transaction, which means it's an all or nothing thing.
If you want this behavior, then you must save changes after each and every record you add.
The reason is that EF doesn't know what constitutes a "transaction" in your data. It might be one row, or it might be several rows in several tables. EF doesn't even try to understand what your transactional requirements might be. You have to do it manually if you want it.

EF Code First and Saving Multiple Records

Just getting to grips with Entity Framework and I can save, add, delete etc a single entity like so:
db.Entry(client).State = EntityState.Modified;
db.SaveChanges();
My question is if I wanted to change several records how should I do this, for example I want to select all Jobs with a Type of 'new' and set the Type to 'complete'. I can select all the jobs easily enough with Linq but do I have to loop through, change them, set the state to modified, save changes, next one etc? I'm sure there is a straightforward way that I just don't know about or managed to find yet.
Are you sure you need to set the EntityState? SaveChanges will DetectChanges before saving. You can't update multiple records at once as you are requesting, but you can loop through, update the value and call save changes after the loop. This will cause 1 connection to the database where all of your updated records will be saved at once.
Yes, you would have to loop through each object, but you don't have to save changes after each one. You can save changes after all the changes have been made and update in a single go. Unless there is some reason you need to save before editing the next record.
However, if you have a simple operation like that, you can also just issue a SQL statement to do it.
context.Table.SqlQuery("UPDATE table set column = 1 where column2 = 3");
Obviously, that more or less bypasses the object graph, but for a simple batch statement there's nothing wrong with doing that.

When to call obtainPermanentIDsForObjects:?

I'm currently having an issue where creating a new object on a background child thread (whose parent is the main UI thread context) and saving causes my NSFetchedResultsController to show two new objects: one with a temporary objectID, and one with a permanent objectID. This seems to be a bug of some sort, unless I'm missing something.
So I thought I would manually obtain permanent IDs for any new objects I create. This fixes the duplicate row issue, but introduces new random errors (such as "could not fulfill fault for object", refering to the new object I created). If anyone has any ideas as to why any of the previously mentioned is happening, please share.
I'm guessing obtainPermanentIDs is a step in the right direction. But when do I call this method? Before saving to the child context? After saving the child and before the parent? After the parent?
Currently my setup is this:
masterMOC - private queue tied to the persistent store, so physical saves happen here
----mainMOC - main queue tied to the UI, child of masterMOC
-------backgroundMOC - private queue, child of mainMOC
So if I create a new object on backgroundMOC, and I intend to immediatly save to disk (which means I'll have to call save: on all three contexts), where should I be calling obtainPermanentIDs?
(or if anyone has a different solution other than calling obtain permanent ids? What problem was this method introduced to solve anyway? Why would I want to call this method?)
Update:
I think I figured out what's going on (it's only a theory though), though not how to solve it. Core Data apparently generates permanent IDs for objects when they are saved physically to disk. So in my case, this won't happen until I call save on the masterMOC. Currently what I do when creating a new object on the backgroundMOC is:
save on backgroundMOC (so that changes are pushed up one level to the mainMOC and the my table view can insert the new rows)
save on mainMOC (so that I can prepare for saving to disk)
save on masterMOC (which finally saves to disk)
What's happening here is that calling save on the backgroundMOC triggers a UI update, and causes the fetched results controller to insert a new object that still has only a temporary ID. But then calling save on masterMOC causes all objects to get assigned permanent IDs, which causes another UI update, inserting another row for this "new" object! By commenting out the last masterMOC save, I no longer see duplicate entries. Am I doing something wrong here, or is this some kind of bug?
Another update: I think I've pretty much confirmed the bug. I call save on the backgroundMOC and then set up a timer to call save on the mainMOC and masterMOC 5 seconds later. Immediatley upon saving to the backgroundMOC, a new row is inserted into my table. 5 seconds later (upon saving main and master), another new row is inserted. (the row inserted first has a temp id, and the newest insert has permanent id).
I had the exact same issue, of course after a particularly difficult and dispiriting day of debugging everything to find out the issue was temporary IDs. :)
I have the exact same structure as you, and I also have subclasses of NSManagedObjectContext to codify the behavior I expect of saves in the background and main contexts – namely, a save in the background context should save the main context (and the main context should sync any objects that changed with the external service, which is irrelevant but worth mentioning as an explanation for why I have two subclasses), and a save in the main context should save the master context.
In my RFSImportContext subclass (equivalent to your backgroundMOC), I implement - save: to call [super save:], then call [self.parentContext performBlock:] (self.parentContext here is equivalent to your mainM)C, where the block calls obtainPermanentIDsForObjects: with the contents of the main context's - updatedObjects and - insertedObjects arrays, then I save the main context.
I no longer have the leaking of temporary objects into my NSFetchedResultsController as you describe. A way to improve the situation a bit would be to use the RFSMainContext subclass (again, equivalent to your mainMOC) to implement - save: to obtain permanent object IDs, save itself, then save the master context. This codifies the behavior that we always want the main context to have permanent IDs for objects in it when it is saved.

Handling background changes with NSFetchedResultsController

I am having a few nagging issues with NSFetchedResultsController and CoreData, any of which I would be very grateful to get help on.
Issue 1 - Updates: I update my store on a background thread which results in certain rows being delete, inserted or updated. The changes are merged into the context on the main thread using the "mergeChangesFromContextDidSaveNotification:" method. Inserts and deletes are updated properly, but updates are not (e.g. the cell label is not updated with the change) although I have confirmed the updates to come through the contextDidSaveNotifcation, exactly like the inserts and deleted. My current workaround is to temporarily change the staleness interval of the context to 0, but this does not seem like the ideal solution.
Issue 2 - Deleting objects: My fetch batch size is 20. If an object is deleted by the background thread which is in the first 20 rows, everything works fine. But if the object is after the first 20 rows and the table is scrolled down, a "CoreData could not fulfill a fault" error is raised. I have tried resaving the context and reperforming the frc fetch - all to no avail. Note: In this scenario, the frc delegate method "didChangeObject...." is not called for the delete - I assume this is because the object in question had not been faulted at that time (as it is was outside the initial fetch range). But for some reason, the context still thinks the object is around, although is has been deleted from the store.
Issue 3 - Deleting sections : When the deletion of a row leads to the deletion of a section, I have gotten the "invalid number of rows in section???" error. I have worked around this by removing the "reloadSection" line from the NSFetchedResultsChangeMove: section and replacing it with "[tableView insertRowsAtIndexPaths...." This seems to work, but once again, I am not sure if this is the best solution.
Any help would be greatly appreciated. Thank you!
I think all your problems relate to the fetched results controller's cache.
Issue 1 is caused by the FRC using the cached objects (whose IDs have not changed.) When you add or remove an object that changes the IDs and forces an update of the cache but changing the attributes of an object doesn't do so reliably.
Issue 2 is caused by the FRC checking for the object in cache. Most likely, the object has an unfaulted relationship that persist in the cache. When you delete it in the background the FRC tries to fault in the object at the other end of the relationship and cannot.
Issue 3: Same problem. The cache does not reflect the changes.
You really shouldn't use a FRC's cache when some object other than the FRC is modifying the data model. You have two options:
(Preferred) Don't use the cache. When creating the FRC set the cache property to nil.
Clear the cache anytime the background process alters the data model.
Of course, two defeats the purpose of using the cache in the first place.
The cache is only useful if the data is largely static and/or the FRC manages the changes. In any other circumstance, you shouldn't use it because FRC need to check the actual data model repeatedly to ensure that it has a current understanding of the data. It can't rely on the object copies it squirreled away because another input may have changed the real objects.
My advice:
Detect the changes needed on the background thread
Post the changes to the main thread as a payload
Make the actual changes and save on the main thread (Managed Object Context on the main thread)
DO use the cache for the FRC; you'll get better performance
Quote from "Pro Core Data for iOS" by Michael Privat, Robert Warner:
"Core Data manages its caches intelligently so that if the results are updated by another call, the cache is removed if affected."