Say I have a custom NSManagedObject Department
and this has a property representing a to-many relationship to employees i.e. NSSet *employees;
.
For a given Department, I want to remove all objects in employees. What is the recommended/best way to do this, please?
So, hypothetically, my code would look like this:
Department.h
@interface Department: NSManagedObject { } @property (retain) NSString *departmentName; @property (retain) NSSet *employees; @end
Department.m
@implementation Department @dynamic departmentName; @dynamic employees;
Employee.h
@interface Employee: NSManagedObject { } @property (retain) NSString *firstName; @property (retain) NSString *lastName; @property (retain) Department *worksIn; @end
doCoreDataStuff
- (void)doCoreDataStuff:sender { //add a department, give it a couple employees, then try to remove those employees NSEntityDescription *deptEntity = [NSEntityDescription entityForName:@"Department" inManagedObjectContext:self.managedObjectContext]; Department *dept = [Department alloc] initWithEntity:deptEntity insertIntoManagedObjectContext:self.managedObjectContext]; NSError *error; dept.departmentName = @"Accounting"; //save more often than normal to see more easily what causes error if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]); NSEntityDescription *empEntity = [NSEntityDescription entityForName:@"Employee" inManagedObjectContext:self.managedObjectContext]; emp.firstName = @"Steve"; emp.lastName = @"Smith"; emp.worksIn = dept; if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]); emp = [[Employee alloc] initWithEntity:empEntity insertIntoManagedObjectContext:self.managedObjectContext]; emp.firstName = @"Natasha"; emp.lastName = @"Johnson"; emp.worksIn = dept; if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]); //all good so far! now will try to delete all employees for this department dept.employees = [NSSet set]; if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]); //"Multiple validation errors occurred." //this also produces the same error [[dept mutableSetValueForKey:@"employees"] removeAllObjects]; if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]); //"Multiple validation errors occurred."
The relationship employees
is not optional so I'm guessing that removing employees from the department means that I am trying to "orphan" the employees i.e. keep employees in the persisted model without an associated department.
So, I think my original question should be reworded to: what is best/recommended way to remove all "child" objects of a "parent" when the children have a non-optional relationship with the parent?
I suspect that the answer is going to be "loop through and delete the employee objects one at a time".
UPDATE
According to an answer and a link to Apple's documentation, I should be able to set the delete rule to "Cascade" and then code like department.employees = [NSSet set];
will work. However, this does not work in my very simple project where I have set the delete rule accordingly.
Thanks
Delete Everything (Delete All Objects, Reset Core Data) One approach to delete everything and reset Core Data is to destroy the persistent store. Deleting and re-creating the persistent store will delete all objects in Core Data.
In some respects, an NSManagedObject acts like a dictionary—it's a generic container object that provides efficient storage for the properties defined by its associated NSEntityDescription instance.
Inverse relationships enable Core Data to propagate change in both directions when an instance of either the source or destination type changes. Every relationship must have an inverse. When creating relationships in the Graph editor, you add inverse relationships between entities in a single step.
Most interactions with Core Data will occur through an instance of NSManagedObjectContext : the portal through which our app will create new entities, save changes, and fetch from the store. The persistent container comes with a NSManagedObjectContext as one of its built-in properties.
If you want to delete the employee elements for a specific department, then you could run a for-in loop like for
for (Employees * theEmployee in department.employees) { [self.managedObjectContext deleteObject:[self.managedObjectContext objectWithID:theEmployee.objectID]]; }
Then save your managed context. IF of course that's what you want, and not remove the relationship between employees and departement; in that case, assigning an empty set would work.
Variation on above:
for (Employee *employeeToDelete in department.employees) { [self.managedObjectContext deleteObject:employeeToDelete]; }
Setting the department's employee relationship to an empty set WILL NOT delete the employees, regardless of the deletion rule. I believe you are misunderstanding the deletion rule. According the apple docs: "A relationship's delete rule specifies what should happen if an attempt is made to delete the source object". Thus, cascading will only take effect if we DELETE the department. By setting the relationship to an empty set, all we are doing is separating the employees from the department, not deleting them. And if that relationship is not set to optional for the employees, this will cause an error when saving. If you want to delete the employees from the department, you can iterate through them as listed above, or set the department relationship to cascade and then delete the department.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With