views:

151

answers:

2

Hi

Another Day, another CoreData problem,...but hopefully the last one now.

Ok here is a copy of what I have :

I have a List of Hotel Guests that stay in one Room and have Preferences. Once ready the user should select a guest and see the data and should also be able to add new guest, select the room (maintained also by application) and select their preferences (where the user can also add new preferences). The guest can have no or many preferences.

So here is what I have so far. I created 3 Entities : - Rooms with roomnumber - Preferences with name - GuestInfo with name -> with these Relationships room (Destination Rooms) and prefs (Destination Preferences with "To-Many Relationship") The prefs is a NSSet when you create a Managed Object Class.

Now I created a UITableViewController to display all the data. I also have an edit and add mode. When I add a new Guest and just fill out the name, everything works fine. But when I want to add the prefs or the room number I get this error :

Illegal attempt to establish a relationship 'room' between objects in different contexts

Now, what confuses me is that when I add a guest and enter just the name, save it, go back and edit it and select the prefs and room number it works ?

I have this line in both ViewControllers to select the room or prefs :

[editedObject setValue:selectedRoom forKey:editedFieldKey];

with this .h :

NSManagedObject *editedObject;
NSString *editedFieldKey;
NSString *editedFieldName;

Again, it works on the editing mode but not when I want to add a fresh object.

And to be sure, this is what I do for adding an new Guest :

- (IBAction)addNewItem 
{

    AddViewController *addViewController = [[AddViewController alloc] initWithStyle:UITableViewStyleGrouped];
    addViewController.delegate = self;
    addViewController.context = _context;

    // Create a new managed object context for the new book -- set its persistent store coordinator to the same as that from the fetched results controller's context.
    NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
    self.addingManagedObjectContext = addingContext;
    [addingContext release];

    [addingManagedObjectContext setPersistentStoreCoordinator:[[_fetchedResultsController managedObjectContext] persistentStoreCoordinator]];

    GuestInfo *info = (GuestInfo *)[NSEntityDescription insertNewObjectForEntityForName:@"GuestInfo" inManagedObjectContext:addingContext];

    addViewController.info = info;

    UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addViewController];

    [self.navigationController presentModalViewController:navController animated:YES];

    [addViewController release];
    [navController release];
}

Anything I have to do to initialize the Room or Prefs ? Hope someone can help me out. Thanks

+2  A: 

The problem you are experiencing is strictly related to your (wrong) use of NSManagedObjectContext objects. Basically you can not use two different managed object contexts to establish a relationship between an object managed by _context and another one managed by addingContext. Creating a new NSManagedObjectContext and passing it to a view controller to be pushed on the stack only works if you are going to add new objects to this contexts but not if you are going to establish relationships between objects belonging to different NSManagedObjectContext objects.

This is why you get the message "Illegal attempt to establish a relationship 'room' between objects in different contexts". To solve the problem, use your _context object to create and save the new objects.

unforgiven
I was writing a similar answer moments ago. The only thing I would add is that it might help the poster to take a look at the iPhone/iPad project template when created with the Core Data option checked. The template shows how a Core Data stack is set up in the App delegate and the NSManagedObjectContext is handed off to the top level (root) view controller--which can then hand it off to the various view controllers it pushes. The MOC should only be created once (as you've stated). +1
Matt Long
thanks for the info, I just tried this by using just the _context, but this did not work. What is working at the moment that I actually just use the addingContext, but I am not sure why, because this does not seem right. Can you give me more detail how I should use the _context or post a rewirte of the addNewItem ? Thanks a lot
elementsense
As Matt suggested, take a look at standard source code for initializing the Core Data stack and passing properly the initialized context to, say, your root view controller. Then, to add a new item, create the new item, allocate and initialize the adding controller and finally pass both the context and the item created to the new controller. Put the controller on the stack and react as needed: if the user wants to save the item, then save it; otherwise, discard the item you created by deleting it and saving the context. Both context and item are properties of the adding controller.
unforgiven
Well, I worked my way through the Book CoreData Example from Apple and thats where my code originated from. I still cant get a suitable solution. If I use the "addingContext" I get the error, but if I re-use _context I add a new item eve if I press cancel. Anyone can tell me how I can avoid this ?
elementsense
If you need to discard an object, delete it and save the context. This can be done using a statement such as [yourManagedObjectContext deleteObject:yourObject]; Then, you save the context as usual.
unforgiven
Thats pretty much what I have as an intermediate solution, in case I cant figure out how to deal with the addingContext. I have GuestInfo *info as a class variable and do [_context deleteObject:info];
eemceebee
+1  A: 

Don't create a new context. You must use the same context that created the room before. Why are you creating a new context every time you add a new item? That's why you get the error. Unless you have threading issues, you should create a single context when your application start or when you load your data, then use that throughout. Even with threading issues, you should generally only have one context per thread (or at least one context per temporary data set).

Jason Coco
The problem is not related to the use of a new context; it is related to trying to establish a relationship between objects belonging to a different context. It is perfectly viable to create a new context each time and it is also standard practice if the objects you are going to create are simple enough. Why? Because if the user changes his/her mind and does not want to save the new object, then you simply discard the newly created context and that's all. No need to delete the object created. This is shown in Apple code. Everything you said about threading and Core Data is true of corse ;-)
unforgiven
I do understand the problem, but I can not get an actual solution for this problem. I have a partly working solution that allows me add new items including the related details, but if I press cancel I am left of with an empty record which I dont know how to remove. Thx btw, if you havent metioned this I would have not noticed at all.
elementsense
To delete a new object you have created, you need to do: [yourManagedObjectContext deleteObject:yourObject];Then, you save the context as usual.
unforgiven