views:

316

answers:

2

I have an in memory managed object context called importMoc that I use to import records (e.g. employees). I have parsed a file and set up the employee objects in importMoc with one very important exception. The user confirms they want to process %d employees but I can't quite figure out how or when to set the "parent" relationship of the employees (e.g. setting their department).

For my purposes they will all be imported into the same department (which the user has already implicitly selected).

Obviously I can't set up the relationships across the two contexts so do I:

  1. Create a department in importMoc and then when I merge changes merge the "import" department with the "real" department?
  2. 2) Merge the employees and then fetch all the freshly imported employees (somehow!!!) and set their department then?
  3. 3) Some other solution that I have overlooked?

It seems like a simple problem but for some reason (laziness? tiredness? stupidity?) I can't figure out how to go about it! Everything I've tried so far seems far too elaborate and complicated!

Thanks in advance!

A: 

1/ assume employee record X has name Y and department id 15 (i.e. it refers the department with id 15 via a relation)

2/ load department with department id 15 from the managed object context

3/ create an employee that refers the object created at (2)

i.e. (note: sample code only, probably won't compile):

NSEntityDescription * departmentED = [NSEntityDescription entityForName:@"Department" inManagedObjectContext:moc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(id == %@)", deptId];

NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:departmentED];
[request setPredicate:predicate];

NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];

/* create employee */

NSEntityDescription * employeeED = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:moc];
NSManagedObject * employeeMO = [[[NSManagedObject alloc] initWithEntity:employeeED insertIntoManagedObjectContext:moc] autorelease];

/* set employee department to department instance loaded above */
[receiptObject setValue:[array objectAtIndex:0] forKey:@"department"];
diciu
Thanks diciu but that doesn't answer my question about the best way to set up relationships between two different contexts (as recommended in the Core Data Programming Guide - Efficiently Importing Data).Due to time restraints I have currently stopped using a second managed object context for the import but I am still curious to know the correct approach.I also found the following recent question but the provided answers aren't very satisfying:http://stackoverflow.com/questions/1554623/illegal-attempt-to-establish-a-relationship-xyz-between-objects-in-different-co
Matthew
+3  A: 

If the Department objects have already been saved to a persistent store, then you can bring them into another managed object context. Since your objects will all have to live in the same persistent store anyway (as cross-store relationships are not allowed), you should be able to simply fetch the ones you need into importMoc.

For example:

foreach (NSDictionary *record in employeeRecords) {
    NSManagedObject *employee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:importMoc];
    // Configure employee however you do that

    NSString *managerID = [record objectForKey:@"someKeyThatUniquelyIdentifiesTheManager"];
    NSFetchRequest *managerFetchRequest = [[NSFetchRequest alloc] init];
    [managerFetchRequest setEntity:[NSEntity entityForName:@"Manager" inManagedObjectContext:importMoc]];
    [managerFetchRequest setPredicate:[NSPredicate predicateWithFormat:@"managerProperty == %@", managerID]];
    NSArray *managers = [importMoc executeFetchRequest:managerFetchRequest error:nil]; // Don't be stupid like me and catch the error ;-)
    [managerFetchRequest release];

    if ([managers count] != 1) {
        // You probably have problems if this happens
    }

    [employee setValue:[managers objectAtIndex:0] forKey:@"manager"];
}

You could also just do a single fetch request to get all of the managers into importMoc and then filter that array to locate the right one each time. That would probably be a lot more efficient. In other words, don't do what I just told you to do above :-)

Alex
So reading the fine manual:If I drop the predicate you said to ignore and call [importMoc executeFetchRequest:error:] I will get an array containing all the managers (for example) that have been saved to a persistent store (regardless of their context) PLUS any managers in the current (importMoc) context regardless of whether they have been saved or not.Am I understanding that correctly?
Matthew
Correct. The context only matters while the objects are in memory. Once they've been saved to the persistent store, they can be fetched into any context.
Alex
Just a quick note to say thanks so much for this answer Alex. I hadn't realised that a fetch would add objects into a separate context if it had the same persistent store coordinator. I had this same problem and this answer worked a treat for me!
John Gallagher