I once again come about my RSS Reader iApp which, at the moment, is designed the following way:
Data model: Category -> Feed -> Post
Master View: Feeds grouped by categories
Detail View: Posts for a given Feed
All of the views and the app delegate only interact with the same _mainMOC (ManagedObjectContext).
Each of the created NSOperations will use its own _localMOC which is connnected to the same NSPersistentStoreCoordinator.
When the App starts, it creates for each of the Feeds a fetchOp NSOperation which it adds to an NSOperationQueue. This should ensure the RSS Feeds will be individually downloaded, parsed, then their contents inserted Post by Post in CoreData.
The AppDelegate observes the NSManagedObjectContextDidSaveNotifications and merges the modifications if the notification it receives is not _mainMOC. It then sends a specific NSNotification to notify the each of the views that a reloadData is required.
Questions:
Do I still need to enclose my _mainMOC save: operations in performBlock blocks?
Should each of my views only use a _localMOC instead of the _mainMOC?
Isn't it redundant to have each view's local NSFetchedResultsController re-perform a fetch before a reloadData?
Should I make any non-read-only Core-Data Operation a queued NSOperation, even at the view level?
How could I make all of this smoother (it still isn't and I still have a few horrendous bugs so redesign is a possibility)...
Thanks for your help.
1) & 2) It doesn't appear these are absolutely required: we are in the main thread, after all.
3) This couldn't hurt: especially if the NSFecthedResultsController uses a variable NSPredicate (see here).
4) I am going to do it anyway as it doesn't hurt.
5) Keep in touch...
Related
I'm trying to edit a Master-Detail View multiple times in Catel.
My question is, if there is no common way in catel to handle a Master-Detail-View and multiple editions using the SaveViewModelAsync and CancelViewModelAsync methods?
The workflow works when my Master-ListItem is no UserControl with it's own ViewModel and when I change the selected Master-ListItem after save or cancel, so that a new ViewModel will be created. But I don't want to null or change the selection after a save/cancel. Also I have maybe to create a UserControl + ViewModel for the Master-ListItems.
Restrictions of the question:
I have got the information from Geert van Horrik from the discussion of his answer, that
multiple edit cycles are not supported directly
one Model should only be accesd by one ViewModel
Problem:
I can use the the SaveViewModelAsync and CancelViewModelAsync only once. After that, the ViewModels which are alive
will get no updates from the Model
don't create a BackUp from the Data, so that the changes can be reverted again
Code to Reproduce:
I have created a WPF Project, where I tested this all.
Possible solution:
I could use the EditableObjectHelper and handle this by myself
I have to instantiate the VewModel again, after the save or cancel, like it happends on a selection change in my Example Code
Restricion of the solution:
If I use a specialized UserControl for the ListItems of the Master-View, 2 ViewModels are looking on 1 Model. After the SaveViewModelAsync the ViewModel doesn't get any Notifications of the Model. So I would have to instantiate this ViewModel again too. But I would Breake the 1-1 relation of the ViewModel-Model.
My conclusion:
It seems, that I have to instantiate all these ViweModels and handle this workflow all by myself. It seems, that the ViewModelLifetimeManagement.PartlyManual (CloseViewModelOnUnloaded is Obsolete) doesn't work here. On selection change, always a new ViewModel will be created, the old one will probably just not be closed.
I don't want to misuse the framework. Maybe I have missed something. I hope somebody can help me or give me a tip, how to handle this.
Catel calls Save / Cancel as soon as the view model get's unloaded. If you want to do "intermediate" saves without changing the data model, you can:
Save the master list which will save the dirty models (you can directly modify the models from within your vm's)
Create a custom command (SaveData) that you can run from an explicit button (or input gesture binding) to save the data without calling SaveAsync on the vm
After the answer of Geert van Horrik, I currently ended up , don't using the SaveViewModelAsync and CancelViewModelAsync methods and handle the save and cancel by my own, so that the notification will not breake. I'm using the GetChildViewModels() method on the ViewModelBase and the EditableObjectHelper.CancelEditObject(Model);
For an iOS application wherein a UIViewController has a collectionView whose datasource (an NSArray) is contained in a separate model class. This array is getting updated [adding / removing items to it] through an NSNotification which in turn requires a web API call [on another thread]. When web service returns information then the datasource is updated and so also UICollectionView needs to be updated. This process is continuous & occurs at random times.
The question is how the UICollectionView's UI updation can be done in a safe manner so that it do not result in issues where UICollectionView start complaining that no. of rows / section before update is not same as after update
Can anybody suggest an approach so that multithreading do not have any impact on UICollectionView and one can safely use UICollectionView's various UI manipulation api like insertSections, insertItemsAtIndexPath etc. In this process the UICollectionView is continuously changing and so also its data source.
Let's say I have a NSManagedObject named «Picture» that I have created via RestKit.
I'm uploading the actual picture file content through a NSOperation. That NSOperation is tracking the upload progress, and saving it in the «progress» attribute of the Picture object.
if(![self isCancelled]) {
Picture *pic = [Picture findFirstByAttribute:#"pictureId" withValue:self.pictureId];
if(![pic isDeleted]) {
pic.progress = [NSNumber numberWithFloat:progress];
[[RKObjectManager sharedManager].objectStore save:NULL];
}
}
My view controller is displaying a list of Picture objects, with the help of a NSFetchedResultsController.
Upload works great, RestKit is doing the thread safety and the merge back to the main NSManagedObjectContext, so the UI is displaying the upload progress as expected.
Imagine now that while uploading the picture the user presses the «cancel» cross, which is also supposed to delete the Picture object.
In the controller, I'm calling a cancel on the NSOperation, which effectively stops the running the operation within milliseconds, and the object is deleted from CoreData on the main thread.
[uploadOperation cancel];
Picture *pic = [Picture findFirstByAttribute:#"pictureId" withValue:self.pictureId];
[pic deleteEntity];
[[RKObjectManager sharedManager].objectStore save:NULL];
Traces show that I get a NSFetchedResultsChangeDelete on my NSFetchedResultsControllerDelegate as expected. However it is immediately followed by a NSFetchedResultsChangeInsert and a NSFetchedResultsChangeUpdate, because the saving of the progress is made really frequently by the NSOperation.
It seems that the NSManagedObjectContext used by the NSOperation is out of sync. It doesn't know that the Picture object is deleted, the call to save: causes an insert and an update.
So in my UI (and in DB), my Picture object remains while it's supposed to be deleted.
Looking at RKManagedObjectStore, it seems that merges are made from background threads (NSOperation) to main thread (my view controller) but not the opposite way.
What's the best approach to fix this issue ?
I think you should probably change the control flow of the app a bit. Deleting the object in the main context while the NSOperation is still in progress sounds like a bad idea. Why don't you e.g. check if the operation was cancelled in the NSOperation itself and delete the image objects from there, using its MOC? In that way you only have writes on one child MOC. The alternative, i.e. updating the child MOC in the operation before every modification defeats the purpose of having separate contexts.
Thinking about it, there is probably some clever way of observing NSManagedObjectContextObjectsDidChangeNotification and merging with an appropriate NSMergePolicy, but then again, I think that's overkill for your case. I'd just handle the entire cancellation and deletion in the same place.
Sounds like the perfect situation to use the Core Data Undo Manager. You'll find a good example over here or in the CoreDataBooks Example provided by Apple.
I can't seem to get a clear answer for this: when you change a transient property, and then call save, should the NSManagedObjectContextDidSaveNotification be triggered? In my notification listener, how can I filter out these notifications that are coming from changes in transient properties?
Here's what I'm trying to do: I want to load up a list of contacts in the main thread, and when it's done, I want to read the images in a background thread from the address book and attach them to the contacts. This works fine on the face of it: after loading from the Contacts entity, I use a dispatch queue to loop through all the contacts, find their image in the Address Book, and save them in Contact's "contactImage" property (which is transient). The dispatch queue then successfully reloads the tableview (on the main thread) and the images show up next to the contacts.
The problem is that if I do anything to the contact that invokes a "save" on even ONE of the managed objects (for e.g. I delete one of the contacts), the NSManagedObjectContextDidSaveNotification is invoked for ALL the contacts. I've found that this is because the contactImage property was changed before ... commenting that the "self.contactImage = img;" line makes the issue go away. This is surprising to me, since I would have thought that the save notification would only be called for non-transient properties.
Can anyone confirm if this is expected behavior? Or am I doing something wrong? If it's expected, how do you filter out the updates to transient properties in the NSManagedObjectContextDidSaveNotification listener? I need to do some post-processing in the listener, and I don' want to do it needlessly for transient property updates. I've checked the changedValues dictionary on the NSManagedObject, but it seems to show empty inside the listener (since only transient properties changed, I'm guessing).
Thanks.
Yesterday,
Transient properties have one key characteristic -- they are managed. You can easily add ivars that are not managed to any NSManagedObject. If you do so, they are not subject to -save: notifications.
A related question: Why are you using a transient ivar? They have some specialized uses; primarily, they are used to trigger property updates throughout the model; i.e. the behavior you are seeing.
A second related question: why are you background fetching all of the images instead of lazy loading them from the Address Book? This looks like a case of premature optimization to me.
Andrew
I'm wondering what strategies people are using to handle the creation and editing of an entity in a master-detail setup. (Our app is an internet-enabled desktop app.)
Here's how we currently handle this: a form is created in a popup for the entity that needs to be edited, which we give a copy of the object. When the user clicks the "Cancel" button, we close the window and ignore the object completely. When the user clicks the "OK" button, the master view is notified and receives the edited entity. It then copies the properties of the modified entity into the original entity using originalEntity.copyFrom(modifiedEntity). In case we want to create a new entity, we pass an empty entity to the popup which the user can then edit as if it was an existing entity. The master view needs to decide whether to "insert" or "update" the entities it receives into the collection it manages.
I have some questions and observations on the above workflow:
who should handle the creation of the copy of the entity? (master or detail)
we use copyFrom() to prevent having to replace entities in a collection which could cause references to break. Is there a better way to do this? (implementing copyFrom() can be tricky)
new entities receive an id of -1 (which the server tier/hibernate uses to differentiate between an insert or an update). This could potentially cause problems when looking up (cached) entities by id before they are saved. Should we use a temporary unique id for each new entity instead?
Can anyone share tips & tricks or experiences? Thanks!
Edit: I know there is no absolute wrong or right answer to this question, so I'm just looking for people to share thoughts and pros/cons on the way they handle master/details situations.
There are a number of ways you could alter this approach. Keep in mind that no solution can really be "wrong" per se. It all depends on the details of your situation. Here's one way to skin the cat.
who should handle the creation of the copy of the entity? (master or detail)
I see the master as an in-memory list representation of a subset of persisted entities. I would allow the master to handle any changes to its list. The list itself could be a custom collection. Use an ItemChanged event to fire a notification to the master that an item has been updated and needs to be persisted. Fire a NewItem event to notify the master of an insert.
we use copyFrom() to prevent having to replace entities in a collection which could cause references to break. Is there a better way to do this? (implementing copyFrom() can be tricky)
Instead of using copyFrom(), I would pass the existing reference to the details popup. If you're using an enumerable collection to store the master list, you can pass the object returned from list[index] to the details window. The reference itself will be altered so there's no need to use any kind of Replace method on the list. When OK is pressed, fire that ItemChanged event. You can even pass the index so it knows which object to update.
new entities receive an id of -1 (which the server tier/hibernate uses to differentiate between an insert or an update). This could potentially cause problems when looking up (cached) entities by id before they are saved. Should we use a temporary unique id for each new entity instead?
Are changes not immediately persisted? Use a Hibernate Session with the Unit of Work pattern to determine what's being inserted and what's being updated. There are more examples of Unit of Work out there. You might have to check out some blog posts by the .NET community if there's not much on the Java end. The concept is the same animal either way.
Hope this helps!
The CSLA library can help with this situation a lot.
However, if you want to self implement :
You have a master object, the master object contains a list of child objects.
The detail form can edit a child object directly. Since everything is reference types, the master object is automatically updated.
The issue is knowing that the master object is dirty, and therefore should be persisted to your database or whatnot.
CSLA handles this with an IsDirty() property. In the master object you would query each child object to see if it is dirty, and if so persist everything (as well as tracking if the master object itself is dirty)
You can also handle this is the INotifyPropertyChanged interface.
As for some of your other questions :
You want to separate your logic. The entity can handle storage of its own properties, and integrity rules for itself, but logic for how different object interact with each other should be separate. Look into patterns such as MVC or MVP.
In this case, creation of a new child object should either be in the master object, or should be in a separate business logic object that creates the child and then adds it to the parent.
For IDs, using GUIDs as the ID can save you quite a bit of problems, because then you don't have to talk to the database to determine a correct ID. You can keep a flag on the object for if it is new or not (and therefore should be inserted or updated).
Again, CSLA handles all of this for you, but does have quite a bit of overhead.
regarding undo on cancel : CSLA has n-level undo implemented, but if you are trying to do it by hand, I would either use your CopyFrom function, or refresh the object's data from the persistance layer on cancel (re-fetch).
i just implemented such a model.but not using NH, i am using my own code to persist objects in Oracle Db.
i have used the master detail concept in the same web form.
like i have master entity grid and on detail action command i open a penal just below the clicked master record row.
On Detail Add mode, i just populate an empty entity whose id were generated in negative numbers by a static field.and on Save Detail button i saved that entity in the details list of the Master Record in Asp.NET Session.
On Detail Edit,View i populated the Detail Panel with selected Detail through ajax calls using Jquery and appended that penal just below the clicked row.
On Save Button i persisted the Master Session (containing list of Details) in database.
and i worked good for me as if multiple details a master need to fill.
also if you like you can use Jquery Modal to Popup that Panel instead of appending below the row.
Hope it helps :)
Thanks,