如何强制UNIDIRECTIONAL与众多关系持续存在

时间:2013-04-02 11:37:16

标签: ios core-data

当多对多关系没有逆时,核心数据存在问题。对相关属性所做的更改不会持续存在。这是我们许多人面临的问题,因为可以通过谷歌搜索找到。

这是问你们是否有人找到了一个技巧/解决方法来实现持久性,除了明显的答案或添加反向关系。

背景

  • 即使文档中不鼓励单向关系,也不禁止它们。该文件仅坚持在没有反向时产生的责任。
  • 核心数据文档中概述了不想反转的原因:当您将大量项目链接到一个实体时,每次添加项目时,反向关系都会加载一个大的NSSet。消耗内存,可能超过允许无理由。

示例

在员工/部门的典型范例中,如果您拥有大量可以属于多个部门的员工,则需要从员工到部门之间的多对多关系。您想要反向,因为每次员工链接到某个部门时,必须加载,更新和保存(非常)大的NSSet。此外,如果从不删除部门实体,则图形完整性易于维护。

请不要回答这是核心数据的一个特征,反向关系是强制性的。这不是这样说的,更像是一个bug而不是一个功能。发布错误报告并未解决当前部署系统的问题。

编辑:加入实体解决方案 本编辑旨在为Dan Shelly的回答提案提供更多启示和讨论。

首先,要回复你的第一个,我不是要尝试多对多而是真正的单向对话。您链接的同一页面的文本略低于您引用的文本:

  

单向关系

     

在两个方向上建立关系并非绝对必要。在某些情况下,例如当多对多关系可能具有非常多的目标对象并且您很少可能遍历关系时(例如,当您可能希望确保您没有不必要的错误时),它可能是有用的。关系目的地的大量物体)。但是,不对两个方向的关系建模,会给您带来很多责任,以确保对象图的一致性,变更跟踪和撤消管理。

如果没有解决办法迫使核心数据自动生成和更新,那么你提出的添加连接实体的解决方案就是一种方法。

IMO,对于我的用例,加入实体甚至不需要与Department有关系。这个to-one是无用的,可以用连接实体的属性替换,保留相关的Department信息,比如它的objectID或其他索引属性来到达它。

即:
DepartmentEmployee:
属性:Dept_ix(整数)
关系:员工(一对一,无效)

3 个答案:

答案 0 :(得分:1)

这是一个很好的问题。

但首先要说的是:它在documentation

中明确说明了
  
    

"重要提示:您必须在两个方向上定义多对多关系 - 也就是说,您必须指定两个关系,每个关系都是另一个关系。您不能只在一个方向上定义多对多关系,并尝试将其用作多对多关系。如果这样做,最终会出现参考完整性问题。"

  

从来没有,Lets描述了问题(结果数据库)
定义多对多关系时,生成的数据库不会添加其他表来映射关系 它只在实体的一端设置一个属性,该属性等于引用它的最后一个项目。

示例:

型号:
实体:部门
关系:无 属性:名称(字符串)

实体:员工
关系:部门(对多,不采取行动)
属性:名称

产生的数据库:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
Z2DEPARTMENTS(int)
ZNAME

ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZNAME

这种结构显然会导致数据不一致。

解决方案是持有一个实体:DepartmentEmployee在两个方向建模多对多关系,但其中一个是单向的(Department - > DepartmentEmployee):

DepartmentEmployee:
关系:部门(一对一,不行动),员工(一对一,无效)

并且您必须在删除部门对象时维护该表。

希望这有一定道理:)

答案 1 :(得分:1)

首先回复你的评论:

  
    

IMO,对于我的用例,加入实体甚至不需要与Department有关系。这个to-one是无用的,可以用连接实体的属性替换,保留相关的Department信息,比如它的objectID或其他索引属性来到达它。

  

这正是部门财产在联合关系中所做的事情 如果您查看生成的SQLite结构,您将看到Employee实体和Department实体之间的其他映射表,只保留int64个ID。

现在,给出的例子是:

  
    

实施例

         

在员工/部门的典型范例中,如果您拥有大量可以属于多个部门的员工,则需要从员工到部门之间的多对多关系。您不希望反向,因为每次员工链接到某个部门时,都必须加载,更新和保存(非常)大的NSSet。此外,如果从不删除部门实体,则图形完整性易于维护。     可以很容易地实现一个简单的一对多关系,没有反向     您可以将其视为关系中“多”方面对象的另一个属性。

  

此示例请求该类型的一对多关系:
员工 - >>部门(员工可能属于多个部门)
反过来是:
系 - >员工
由于我们不能在没有逆的情况下实现多对多关系,因此我们必须实现关系的一对一,以确保我们遵守框架的实现。

重新迭代:
通过文档我们知道,如果没有定义反向关系,任何多对多关系都不会持续存在 ==>
由于我们喜欢在没有逆的情况下对关系进行建模,因此我们只将其建模为耦合的一个部分(将其建模为一个to-many将违反框架承诺的持久性)
可以认为它对于定义文件夹中的文件(文件可能不属于多个文件夹)或父子关系很有用。
==>
我们必须将关系定义为:
部门 - >员工(由于只能雇用一名员工的部门并不是真正的部门,因此没有多大意义)

从另一位天使那里看(负面证明):
假设我们想反对框架并定义一个没有反转的MANY-to-many关系 ==>
这意味着我们只会在一个方向上实现它,留下一个......多对多的关系或者......很多关系 ==>
这是同样的事情不是它(从实体1到实体2的多对多关系)
==>
现在,如果我们有一对多的关系并且我们选择不实现它的逆,我们可以选择实现to-many部分? 不,我们不能,这看起来只是MANY-to-many关系的一半 ==>
我们必须实施它的一部分。

为了使一些更有意义,我会表现出更合乎逻辑的:
系 - >>员工 因此,我们对这种一对多关系的实施将是:
部< - 雇员

这将产生以下SQLite DB结构:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
ZNAME

ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZDEPARTMENT(int)
ZNAME

我们现在可以在Department上定义一个fetched属性来获取属于它的所有员工:
employees谓词:department == $FETCH_SOURCE

您可以在部门的prepareForDeletion方法中执行此关系(未经测试):
(您将首先在部门设置userInfo字典以保持执行类型) (我把“拒绝”规则的执行留给了读者:D)

- (void) prepareForDeletion
{
    [super prepareForDeletion];
    NSEntityDescription* entity = [self entity];
    NSDictionary* dict = [entity userInfo] ;
    if ([dict count]) {
        [dict enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL *stop) {
            NSArray* arr = [self valueForKey:key];
            if( [value isEqualToString:@"cascade"]) {
                for (NSManagedObject* obj in arr) {
                    [[self managedObjectContext] deleteObject:obj];
                }
            } else if ( [value isEqualToString:@"nullify"] ) {
                NSArray* arr = [self valueForKey:key];
                for (NSManagedObject* obj in arr) {
                    [obj setValue:nil forKey:@"department"];
                }

            }
        }];
    }
}

正如我所看到的,关于反向关系,你可以做的就是这一切。 如果您仍然认为自己需要多对多关系,请参阅我的其他答案。

的问候,
丹。

答案 2 :(得分:0)

您是否考虑过完全废除关系并以编程方式管理员工的外键?

如果你有一个用户界面从现有部门列表(选择列表等)设置属性,你只需从该列表中取出主键并将其指定为员工的departmentID属性即可。

然后,您应该能够在Employee对象上实现validateDepartmentID:error方法,该方法检查给定的departmentID是否有效(即在获取的部门列表中)和/或不为空您在员工和部门之间保持参照完整性。

在部门中获取员工列表时,您可以使用获取的属性或向部门添加实例方法,该部门返回包含部门员工列表的NSFetchedResultsController实例。

您需要做的唯一事情是在您的Department类中注入一些删除逻辑(可能在-prepareForDeletion上)以更新任何受影响的子记录上的departmentID。那个取决于你的业务逻辑。

Apple docs on property validation封面-prepareForDeletion-validateValue:forKey:error,如果您不熟悉它们。