views:

247

answers:

2

SOLVED

I misspelled the property department -> Department. Thanks for the answers and a big SORRY

Hi,

Imagine the obvious model of employees and departments, where each employee must refer to a single department, and each department may have none to many employees.

For Employee I have a pointer to Department, and I set its value to the address of the object of the relevant Department.

For Department I have a NSSet, to which I can add employees.

The problem is that when I create a new employee and a new department, the objects exist in the memory at some place. The reference from Employee to Department, and vice versa, contains these addresses. When I'll fetch the data it will be located at a different location. So it is not clear how the connection between these objects is done.

Here is the code as I understand it should be:

    // Fetch Plist object
    NSDictionary *plistDictionary = [[NSDictionary alloc] initWithDictionary:plistData];

    // Create Department Entity
    Department *department = (Department *)[NSEntityDescription 
                                insertNewObjectForEntityForName:@"Department" 
                                inManagedObjectContext:managedObjectContext];
    // Set properties
    [department setName:[plistDictionary valueForKey:@"department"]];

    // Create Employee entity
    Employee *employee = (Employee *)[NSEntityDescription 
                             insertNewObjectForEntityForName:@"Employee" 
                             inManagedObjectContext:managedObjectContext];

    // Set properties
    [employee setFileName:[plistDictionary valueForKey:@"picture"]];
    [employee setName:[plistDictionary valueForKey:@"name"]];

    // Set relationship
    [employee setDepartment:department];
    [department addEmployee:employee];

    // Save data to core data
    [self saveAction];

And here is the Error I get:

      2010-04-08 16:06:11.295 Paparazzi2[2656:207] ERROR:saveAction. Unresolved Core Data Save error Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x3d1c6f0 "Operation could not be completed. (Cocoa error 1570.)", {
NSLocalizedDescription = "Operation could not be completed. (Cocoa error 1570.)";
NSValidationErrorKey = department;
NSValidationErrorObject = <Employee: 0x3d13a30> (entity: Employee; id: 0x3d16670 <x-coredata:///Employee/t8C950118-C388-4020-8CD9-1F49138A94193> ; data: {
picture = "mike.jpg";
name = "Mike Smith";
department = nil;

}); }

Thanks, Tzur.

A: 

You can't use nil in an NSDictionary. If you haven't synthesized classes to represent your managed objects, you have to use the NSNull class. So, assuming employee is an instance of NSManagedObject and represents your Employee entity:

[employee setValue:[NSNull null] forKey:@"department"];

This is used to sever whatever relationship this instance of Employee had with its previous department. If you do set this to nil, however, the department attribute must be optional or you will have a validation error when you try to save your object graph (context).

Jason Coco
Thanks, but the managed object (Employee) is not a dictionary. It has a property "Department *department" which I set with "[employee setDepartment:department]"
Tzur Gazit
@Tzur Gazit: Can you post your code for that? Just edit your question above and append it to the end.
Jason Coco
I added code sample. Note that when I remove the reference to Department from Employee - It works.
Tzur Gazit
+2  A: 

Hey Tzur, one problem with the code you provide is that you're trying to assign the department relationship a value that is not a managed object.

You should first create the Department object if it doesn't already exist, and use that object as the value of the department relationship.

I would replace this line:

[employee setDepartment:[object valueForKey:@"department"]];

With this:

Department *myDepartment = [self fetchDepartmentNamed:[object objectForKey:@"department"]
                               inManagedObjectContext:[self managedObjectContext]];
if (!myDepartment) {
  /* no existing department with that name.. create one */
  myDepartment = (Department *)[NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:[self managedObjectContext]];
  [myDepartment setName:[object objectForKey:@"department"]];
}
/* assign the relationship to the department managed object */
[employee setDepartment:myDepartment];

This assumes you have defined a method -(Department *)fetchDepartmentNamed:inManagedObjectContext:] that will build a fetch request and get a Department with the name you provide (also assumes your department names are unique)

Alternatively, you could just add the employee to myDepartment's "employees" relationship, and it would achieve the same result.


Ok, so you have updated your code, and now you have this:

// Set relationship
[employee setDepartment:department];
[department addEmployee:employee];

You should have done, one or the other... not both. Check in the managed object model that you have selected the "Inverse:" of the relationship for both entities. If you set one of the relationships, Core Data will manage the reverse (inverse) of the relationship.

ohhorob