Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iPhone Core Data: Cascading delete across a many-to-one relationship

Tags:

I have two classes A and B with a many-to-one relationship from A to B (multiple A objects may reference the same B). The question is, if the delete rule on the A side is Cascade, will B be deleted only when the last referencing A is deleted or will it be deleted the first time an associated A is deleted. The delete rule for the B side of the relationship is Nullify if that matters.

Also, I read in the Core Data docs that the Optional flag matters in some cases. But it wasn't clear how the relationships they were illustrating related to my case. They were talking about a containment case (B is owned by A) whereas my case is one of subscription/association (B is related to A).

I could simply manage deletion programmaticaly in the code but wanted to allow Core Data to do the right thing if possible. But it's not clear that the garbage collection semantics that I'm looking for are supported in Core Data.

Any suggestions?

like image 830
David Goodine Avatar asked Feb 04 '10 23:02

David Goodine


People also ask

What is cascade delete rules in core data?

The Cascade delete rule is useful if the data model includes one or more dependencies. Let me give you an example. If a note should always have a category, the deletion of a category should automatically delete the notes associated with that category.

Should I use delete cascade?

ON DELETE CASCADE is fine, but only when the dependent rows are really a logical extension of the row being deleted. For example, it's OK for DELETE ORDERS to delete the associated ORDER_LINES because clearly you want to delete this order, which consists of a header and some lines.


2 Answers

I had the same goal as you apparently had (delete B as soon as the last referenced A is deleted). It took me longer than expected to get this right. Particularly because

  • At the time A prepares for deletion, the to-many relationship in B might not be updated yet, so you can't just count the A referenced in B.
  • isDeleted on A seems to be already set during -prepareForDeletion

Here's what worked for me if anybody's interested (I'll use Department <-->> Employee because it's easier to read):

In Employee:

- (void)prepareForDeletion {     // Delete our department if we we're the last employee associated with it.     Department *department = self.department;     if (department && (department.isDeleted == NO)) {         NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isDeleted == NO"];         NSSet *employees = [department.employees filteredSetUsingPredicate:predicate];          if ([employees count] == 0) {                        [self.managedObjectContext deleteObject:department];         }      } } 

Other people have suggested putting this logic into -willSave in Department. I prefer the solution above since I might actually want to save an empty department in some cases (e.g. during manual store migration or data import).

like image 129
Lukas Avatar answered Oct 02 '22 09:10

Lukas


Here's a Swift 4 version of Lukas' answer:

public override func prepareForDeletion() {     guard let department = department else { return }      if department.employees.filter({ !$0.isDeleted }).isEmpty {         managedObjectContext?.delete(department)     } } 
like image 26
JanApotheker Avatar answered Oct 02 '22 07:10

JanApotheker