web-dev-qa-db-fra.com

Comment supprimer tous les objets via une relation de données centrale?

Supposons que j'ai un objet NSManagedObject Department personnalisé et qu'il possède une propriété représentant une relation à plusieurs entre les employés, à savoir NSSet *employees;.

Pour un service donné, je souhaite supprimer all objets dans les employés. Quelle est la meilleure façon de le faire, s'il vous plaît?

Donc, hypothétiquement, mon code ressemblerait à ceci:

Département.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."

La relation employees n’est pas facultative, je suppose donc que le fait de supprimer des employés du service signifie que j’essaie d’organiser les employés "orphelins", c’est-à-dire de conserver les employés dans le modèle persistant sans service associé.

Donc, je pense que ma question initiale devrait être reformulée comme suit: quel est le meilleur/recommandé moyen de supprimer tous les objets "enfants" d'un "parent" lorsque les enfants ont une relation non optionnelle avec le parent?

Je soupçonne que la réponse va être "faire une boucle et supprimer les objets employé un par un".

METTRE À JOUR

Selon une réponse et un lien vers la documentation d'Apple, je devrais pouvoir définir la règle de suppression sur "Cascade", puis un code comme department.employees = [NSSet set]; fonctionnera. Cependant, cela ne fonctionne pas dans mon projet très simple où j'ai défini la règle de suppression en conséquence.

Merci

19
stifin

Si vous souhaitez supprimer les éléments d'employé d'un service spécifique, vous pouvez exécuter une boucle for-in comme pour

for (Employees * theEmployee in department.employees) {
  [self.managedObjectContext deleteObject:[self.managedObjectContext objectWithID:theEmployee.objectID]]; 
}

Enregistrez ensuite votre contexte géré. SI, bien sûr, c'est ce que vous voulez et ne supprimez pas la relation entre les employés et le département; dans ce cas, assigner un ensemble vide fonctionnerait.

Variation ci-dessus:

for (Employee *employeeToDelete in department.employees) {
    [self.managedObjectContext deleteObject:employeeToDelete];
}
16
Remy Vanherweghem

Définir la relation d'employé du service sur un ensemble vide NE SUPPRIMERA PAS les employés, quelle que soit la règle de suppression. Je crois que vous comprenez mal la règle de suppression. Selon la documentation Apple: "La règle de suppression d'une relation spécifie ce qui doit se passer si une tentative de suppression de l'objet source est effectuée". Ainsi, la mise en cascade ne prendra effet que si nous supprimons le service. En définissant la relation sur un ensemble vide, nous ne faisons que séparer les employés du service, et non les supprimer. Et si cette relation n'est pas définie sur facultative pour les employés, cela entraînera une erreur lors de la sauvegarde. Si vous souhaitez supprimer les employés du service, vous pouvez les parcourir comme indiqué ci-dessus ou définir la relation entre les services sur cascade, puis supprimer le service.

15
Scott Pfeil

J'ai aussi eu quelque chose comme ci-dessous mais cela n'a pas fonctionné ...

- (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;

}

De toute évidence, la couche de base de données ne peut pas supprimer les sons, puis la collection en une fois. La définition de la règle de suppression de la relation sur "cascade" a résolu mon problème et laissez-moi utiliser simplement:

[context deleteObject:collection];

Si vous voulez économiser du temps de lecture détaillé à certaines personnes, cochez cette case.

Comme Fervus l’a mentionné plus haut, ce lien peut également être utile aux professionnels: Propagate supprime immédiatement dans Core Data

1
DynamicDan