核心数据删除多对多关系的规则

时间:2012-04-20 21:32:16

标签: core-data nspredicate object-model

我有一个带有Container和Item实体的Core Data模型。 Container可以包含零个或多个项目。一个Item必须属于至少一个Container(但它可以在多个Container中。)

关系看起来像这样:

Container:
  Relationship: items, Destination: Item, Inverse: itemContainers
  Optional, To-Many Relationship
  Delete Rule: Nullify

Item:
  Relationship: itemContainers, Destination: Container, Inverse: items
  Not-Optional, To-Many Relationship
  Delete Rule: Cascade

删除Container时会出现问题。该容器中的Item对象已更新,但如果该项目仅存在于一个容器中,则itemContainers属性是一个没有对象的集合。保存对象图失败,因为该空集违反了itemContainers的Item的非可选设置。

当然,使用NSPredicate(例如“itemContainers。@ count == 0”)找到带有空itemContainers的Item对象很容易,但似乎应该有一种方法可以配置模型来自动执行此操作。 / p>

那么有更容易/更好的方法吗?

5 个答案:

答案 0 :(得分:4)

我在上面针对类似的问题尝试了Tony Arnold's answer,但是在一次删除多个“容器”时发现了问题(这是在OS X 10.8.2上)。在保存托管对象上下文之前,容器不会从[item itemContainers]中删除,因此count仍然高于1,item永远不会被删除。

我在-[NSManagedObject isDeleted]上使用NSManagedObject和类别方法提出了以下解决方案。

档案NSManagedObject+RJSNondeletedObjects.h

#import <CoreData/CoreData.h>

@interface NSManagedObject (RJSNondeletedObjects)

- (NSSet *)RJS_nondeletedObjectsForToManyKeyPath:(NSString *)keyPath;
- (BOOL)RJS_hasOtherNondeletedObjectsForToManyKeyPath:(NSString *)keyPath;

@end

档案NSManagedObject+RJSNondeletedObjects.m

#import "NSManagedObject+RJSNondeletedObjects.h"

@implementation NSManagedObject (RJSNondeletedObjects)

- (NSSet *)RJS_nondeletedObjectsForToManyKeyPath:(NSString *)keyPath
{
    NSSet * result = nil;

    id allObjectsForKeyPath = [self valueForKeyPath:keyPath];

    if ( ![allObjectsForKeyPath isKindOfClass:[NSSet class]] ) return result;

    result = [(NSSet *)allObjectsForKeyPath objectsPassingTest:^BOOL(id obj, BOOL *stop)
    {
        BOOL testResult = ![obj isDeleted];
        return testResult;
    }];

    return result;
}

- (BOOL)RJS_hasOtherNondeletedObjectsForToManyKeyPath:(NSString *)keyPath
{
    BOOL result = NO;

    // self will be in the set of nondeleted objects, assuming it's not deleted. So we need to adjust the test threshold accordingly.
    NSUInteger threshold = [self isDeleted] ? 0 : 1;
    NSSet * nondeletedObjects = [self RJS_nondeletedObjectsForToManyKeyPath:keyPath];
    result = ( [nondeletedObjects count] > threshold );

    return result;
}

@end

Container课程

...
#import "NSManagedObject+RJSNondeletedObjects.h"
...
- (void)prepareForDeletion
{
    NSSet *childItems = [self items];

    for (Item *item in childItems) {
        if ([item RJS_hasOtherNondeletedObjectsForToManyKeyPath:@"containers"]) {
            continue;
        }

        [managedObjectContext deleteObject:item];
    }
}

答案 1 :(得分:1)

我知道它不像Core Data提供的配置选项一样干净,但是我已经部署了一些项目,其中Container对象在删除时会循环显示它的子Item实体,检查如果他们有0 itemContainers(在'Container.m'里面):

- (void)prepareForDeletion
{
    NSSet *childItems = [self items];

    for (Item *item in childItems) {
        if ([[item itemContainers] count] > 1) {
            continue;
        }

        [managedObjectContext deleteObject:item];
    }
}

答案 2 :(得分:0)

在我的应用程序中,我将项目的容器关系设为可选,并通过“智能容器”访问这些无容器项目。

如果你不想这样,我怀疑你只需要处理保存失败,并删除违规对象。

我越来越多地将核心数据的方法转变为防御性数据:假设验证失败,并准备好处理它。在集成iCloud同步时变得更加重要。

答案 3 :(得分:0)

我认为你不能在你的模型中指定这种行为,但是我可以验证你的容器中的itemContainers的数量

而不是进行那次获取。
 - (void)removeItemObject:(Item *)value
{...
if(![[value itemContainers]count])
  [context deleteObject:value];
...
}

答案 4 :(得分:0)

我喜欢这样做:

- (void)didChangeValueForKey:(NSString *)inKey withSetMutation:(NSKeyValueSetMutationKind)inMutationKind usingObjects:(NSSet *)inObjects
{
    [super didChangeValueForKey:inKey withSetMutation:inMutationKind usingObjects:inObjects];

    if ([inKey isEqualToString:@"YOURRELATIONSHIP"] && self.YOURRELATIONSHIP.count == 0) {
        [self.managedObjectContext deleteObject:self];
    }
}