setIncludesSubentities: in an NSFetchRequest is broken for entities across multiple persistent store

Posted by SG on Stack Overflow See other posts from Stack Overflow or by SG
Published on 2010-05-03T20:49:41Z Indexed on 2010/05/04 0:58 UTC
Read the original article Hit count: 365

Prior art which doesn't quite address this: http://stackoverflow.com/questions/1774359/core-data-migration-error-message-model-does-not-contain-configuration-xyz

I have narrowed this down to a specific issue. It takes a minute to set up, though; please bear with me.

The gist of the issue is that a persistentStoreCoordinator (apparently) cannot preserve the part of an object graph where a managedObject is marked as a subentity of another when they are stored in different files. Here goes...

1) I have 2 xcdatamodel files, each containing a single entity. In runtime, when the managed object model is constructed, I manually define one entity as subentity of another using setSubentities:. This is because defining subentities across multiple files in the editor is not supported yet. I then return the complete model with modelByMergingModels.

//Works! 
[mainEntity setSubentities:canvasEntities];
NSLog(@"confirm %@ is super for %@", [[[canvasEntities lastObject] superentity] name], [[canvasEntities lastObject] name]);
//Output: "confirm Note is super for Browser"

2) I have modified the persistentStoreCoordinator method so that it sets a different store for each entity. Technically, it uses configurations, and each entity has one and only one configuration defined.

//Also works!
for ( NSString *configName in [[HACanvasPluginManager shared].registeredCanvasTypes valueForKey:@"viewControllerClassName"] ) {
storeUrl = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:[configName stringByAppendingPathExtension:@"sqlite"]]];
//NSLog(@"entities for configuration '%@': %@", configName, [[[self managedObjectModel] entitiesForConfiguration:configName] valueForKey:@"name"]);
//Output: "entities for configuration 'HATextCanvasController': (Note)"
//Output: "entities for configuration 'HAWebCanvasController': (Browser)"
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:configName URL:storeUrl options:options error:&error])
//etc

3) I have a fetchRequest set for the parent entity, with setIncludesSubentities: and setAffectedStores: just to be sure we get both 1) and 2) covered. When inserting objects of either entity, they both are added to the context and they both are fetched by the fetchedResultsController and displayed in the tableView as expected.

// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesSubentities:YES]; //NECESSARY to fetch all canvas types
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20]; // Set the batch size to a suitable number.
[fetchRequest setAffectedStores:[[managedObjectContext persistentStoreCoordinator] persistentStores]];
[fetchRequest setReturnsObjectsAsFaults:NO];

Here is where it starts misbehaving: after closing and relaunching the app, ONLY THE PARENT ENTITY is fetched.

If I change the entity of the request using setEntity: to the entity for 'Note', all notes are fetched. If I change it to the entity for 'Browser', all the browsers are fetched. Let me reiterate that during the run in which an object is first inserted into the context, it will appear in the list. It is only after save and relaunch that a fetch request fails to traverse the hierarchy.

Therefore, I can only conclude that it is the storage of the inheritance that is the problem. Let's recap why:

    - Both entities can be created, inserted into the context, and viewed, so the model is working
    - Both entities can be fetched with a single request, so the inheritance is working
    - I can confirm that the files are being stored separately and objects are going into their appropriate stores, so saving is working
    - Launching the app with either entity set for the request works, so retrieval from the store is working
    - This also means that traversing different stores with the request is working
    - By using a single store instead of multiple, the problem goes away completely, so creating, storing, fetching, viewing etc is working correctly.

This leaves only one culprit (to my mind): the inheritance I'm setting with setSubentities: is effective only for objects creating during the session.

Either objects/entities are being stored stripped of the inheritance info, or entity inheritance as defined programmatically only applies to new instances, or both. Either of these is unacceptable. Either it's a bug or I am way, way off course.

I have been at this every which way for two days; any insight is greatly appreciated. The current workaround - just using a single store - works completely, except it won't be future-proof in the event that I remove one of the models from the app etc. It also boggles the mind because I can't see why you would have all this infrastructure for storing across multiple stores and for setting affected stores in fetch requests if it by core definition (of setSubentities:) doesn't work.

© Stack Overflow or respective owner

Related posts about nsfetchrequest

Related posts about nsfetchedresultscontrolle