6
votes

I have a problem with NSBatchDeleteRequest seems that is not possible to delete relationship references.

I have two entities:

  • News
  • Categories

where a category can have multiple news.

Now, when I try to delete all the objects in the core data using NSBatchDeleteRequest with the following code, then looking into the sqlite file seems that all categories are deleted, all news are deleted, but the relationship between categories and news persists, and this cause faults.

Here the delete function:

NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:entityName];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:fetchRequest];
[delete setResultType:NSBatchDeleteResultTypeCount];
NSError *error;
NSBatchDeleteResult *results = [deleteContext executeRequest:delete error:&error];

Any idea on how to fix this?

3
What delete rule are you using?Shizam
@Shizam I'm using NullifySerluca
How many records? Less than 10.000? You might as well use the object graph.Mundi
@Mundi a lot, right now the app takes 6 minutes to logging out with object graph.Serluca
This does not sound right. You are probably saving in a loop or only once at the end. You need to get the batch size right.Mundi

3 Answers

3
votes

Set shouldDeleteInaccessibleFaults: to YES and inaccessible/unfulfillable faults will be deleted. This solves the immediate problem.

The WWDC 2015 session What's New in Core Data talks about this a little bit. Both NSBatchDeleteRequest and NSBatchUpdateRequest modify the persistent store without the participation of the NSManagedObjectContext - which will result in the context's view of the data being inconsistent with the store.

The in-memory copy of the deleted object needs to be updated in the NSManagedObjectContext - have the batch delete request return the object IDs of the deleted objects and tell the NSManagedObjectContext to refresh those IDs.

This would look something like this:

[managedObjectContext performBlock:^{
    NSBatchDeleteRequest    batchDeleteRequest  = [NSBatchDeleteRequest alloc] initWithFetchRequest:fetchRequest];
    NSBatchDeleteResult     result              = nil;

    result = [managedObjectContext executeRequest:batchDeleteRequest error:&error];

    if ([[result result] count] > 0){
        [managedObjectContext performBlock:^{
            NSArray<NSManagedObjectID *> *objectIDs = (NSArray<NSManagedObjectID *>)[result result];
            [objectIDs enumerateObjectsUsingBlock:^(NSManagedObjectID *objID, NSUInteger idx, BOOL *stop) {
                NSError *error = nil;
                NSManagedObject *obj = [managedObjectContext existingObjectWithID:objID error:&error];
                if (![obj isFault] ) {
                    [managedObjectContext refreshObject:obj mergeChanges:YES];
                }
            }];
        }];
    }
}];

When the batch delete runs, relationships will be deleted or nullified, but a cascading set of delete rules may not be executed, and validation rules will not be executed - it is up to your application to ensure data integrity when using any of the batch change requests.

Your data model may require you to issue multiple delete requests to prevent related objects from being orphaned but still findable. For example, you may need a second batch delete to locate previously related entities that now have empty relationships. The predicate for such a request may look like:

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"toMany.@count == 0"];

Or may use a subquery, etc.

3
votes

You can probably do [manageObjectContext reset];

0
votes

I think the best solution might be to first delete the categories in the object graph, thus nullifying all relationships.

After that you could proceed with the NSBatchDeleteRequest for the news items.