web-dev-qa-db-ja.com

Core Dataリレーションシップを介してすべてのオブジェクトを削除するにはどうすればよいですか?

カスタムNSManagedObjectDepartmentがあり、これには従業員との多対多の関係を表すプロパティ、つまりNSSet *employees;があるとします。

特定の部門について、従業員のallオブジェクトを削除したいと思います。これを行うための推奨/最良の方法は何ですか?

したがって、仮想的に、私のコードは次のようになります。

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."

関係employeesはオプションではないため、部門から従業員を削除するということは、従業員を「孤立」させようとしている、つまり、関連する部門のない永続モデルに従業員を維持しようとしていることを意味すると思います。

したがって、私の元の質問は次のように言い換える必要があると思います。子が親とオプションではない関係にある場合に、「親」のすべての「子」オブジェクトを削除するための最良/推奨される方法は何ですか?

答えは「従業員オブジェクトを一度に1つずつループして削除する」だろうと思います。

[〜#〜] update [〜#〜]

回答とAppleのドキュメントへのリンクによると、削除ルールを「カスケード」に設定すると、department.employees = [NSSet set];のようなコードが機能するはずです。ただし、これは、削除ルールを適切に設定した非常に単純なプロジェクトでは機能しません。

ありがとう

22
stifin

特定の部門の従業員要素を削除する場合は、forのようなfor-inループを実行できます。

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

次に、管理コンテキストを保存します。もちろん、それが必要な場合は、従業員と部門の関係を削除しないでください。その場合、空のセットを割り当てると機能します。

上記のバリエーション:

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

部門の従業員関係を空のセットに設定しても、削除ルールに関係なく、従業員は削除されません。削除ルールを誤解していると思います。 Apple docs: "リレーションシップの削除ルールは、ソースオブジェクトを削除しようとした場合にどうなるかを指定します"。したがって、カスケードは、部門を削除した場合にのみ有効になります。空のセットとの関係では、従業員を部門から分離するだけで、削除することはありません。その関係が従業員に対してオプションに設定されていない場合、保存時にエラーが発生します。削除する場合は、部門の従業員は、上記のように繰り返し処理するか、部門の関係をカスケードに設定してから部門を削除することができます。

19
Scott Pfeil

私も以下のようなものを持っていましたが、うまくいきませんでした...

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

明らかに、データベースレイヤーはサウンドとコレクションを一度に削除することはできません。 「カスケード」との関係に削除ルールを設定すると、問題がうまく解決し、次のように使用できます。

[context deleteObject:collection];

一部の人々の詳細な読書時間を節約したい場合は、これを答えとしてマークしてください。

Fervusが上で述べたように、このリンクはプロにも役立つかもしれません: コアデータで削除をすぐに伝播する

1
DynamicDan