views:

563

answers:

2

I am using Apple's CoreDataBooks sample application as a basis for pulling data into a secondary managed object context in the background, and then merging that data into the primary managed object context.

The data I am pulling in is a Book entity with a to-one relationship with an Owner entity (called "owner"). The Owner entity has a to-many relationship with the Book (called "books").

My data is an XML document of the form:

<Owner>
  <Name>alexpreynolds</Name>
  <ID>123456</ID>
</Owner>
<Books>
  <Book>Book One</Book>
  <Book>Book Two</Book>
  ... 
  <Book>Book N</Book>
</Books>

Book One through Book N are associated with one Owner ("alexpreynolds, 123456").

I am parsing this into an Owner instance and an NSMutableSet made up of Book instances.

When I attempt to save the first time, it saves fine and the merged data shows up in the table view.

On the second save, however, when the XML content contains a new book, it doesn't work.

Here's what happens:

I then attempt to load in an XML document that contains a new Book not already in the primary managed object context. The new Book is using the same Owner as that which is associated with the other Books.

I have routines that pick out this unique Owner managed object (which I already have in my primary managed object context) and the unique Book that is not found in the primary MOC.

From this, I create a new Book object in the secondary MOC, and I set its "owner" relationship to point to the unique Owner I found in the primary MOC.

When I save, I get the following error:

*** Terminating app due to uncaught 
exception 'NSInvalidArgumentException', 
reason: 'Illegal attempt to establish a 
relationship 'owner' between objects in 
different contexts 

(source = <Book: 0x7803590> 
(entity: Book; id: 0x7802ae0 <x-coredata:///
Book/t527F06B2-3EB5-47CF-9A29-985B0D3758862>
; data: {
creationDate = 2009-10-12 06:01:53 -0700;
name = nil;
nameInitial = nil;
operations = (
);
owner = nil;
type = 0;
}) , 

destination = <Owner: 0x78020a0> (entity: 
Owner; id: 0x3a56f80 <x-coredata://043AF2F0-1AD0-
4078-A5E8-E9D7071D67D1/Owner/p1> ; data: {
books = "<relationship fault: 0x7801bf0 'books'>";
displayName = alexpreynolds;
ownerID = 123456;
}))'

How do I create a new Book entity in the secondary MOC, so that I can still associate it with a pre-existing Owner in the primary MOC?

+1  A: 

As the error says, you're not allowed to have a relationship in one Core Data object whose value is set to another object held in a different context. One way you can get around this is to wait until after you save the new object and merge it back into the primary context, then set the owner relationship as appropriate (since both objects are now in the same context, there's no problem with that).

Tim
Following the Core Data Books example, the secondary MOC is released after the merge occurs. If I instead make the secondary MOC an instance variable, the primary MOC's owner is referring to books in the secondary MOC, which gives the error message. If I have a secondary MOC-owner that refers to books in the secondary MOC, when I merge I now have duplicate owner instances, which is not correct. Ideally I would be able to associate with an existing owner. If you have time, could you please explain your answer in a little more detail?
Alex Reynolds
+2  A: 

You can't have relationships between objects in different managed object contexts. So one way of getting around that is to bring the object into the managed object context.

For example:

NSManagedObject *book = // get a book in one MOC
NSManagedObject *owner = // get an owner in a different MOC
[[owner mutableSetValueForKey:@"books"] addObject:[owner managedObjectContext:objectWithID:[book objectID]]];

So what you're doing is actually fetching the Book into the same managed object context with owner. Keep in mind, though, that this is only possible if book has already been saved. The managed object context is going to look for the object in the persistent store, so it has to be saved first.

Alex