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
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.
I also had something like below but it didn't work...
- (BOOL)deleteCollection:(Collection *)collection {
// Grab the context
NSManagedObjectContext *context = [self managedObjectContext];
NSError *error = nil;
[collection removeSounds:collection.sounds];
[context deleteObject:collection];
// Save everything
if ([context save:&error]) {
return YES;
}
return NO;
}
Obviously the database layer can't delete the sounds and then the collection at once. Setting the delete rule on the relationship to 'cascade' nicely solved my problem and let me use just:
[context deleteObject:collection];
If you want to save some people detailed reading time then just mark this as the answer.
As Fervus stated above, this link may also be helpful for the pros: Propagate deletes immediately in Core Data
精彩评论