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

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

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

背景

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

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

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

编辑:加入实体解决方案此编辑旨在为Dan Shelly的答案提议提供更多信息和讨论。

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

单向关系

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

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

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

即:
DepartmentEmployee:
属性:Dept_ix(整数)
关系:员工(to-one,nullify)

这是一个很好的问题。

但首先要做的事情是:
它在文档中明确说明 :

“重要:您必须在两个方向上定义多对多关系 – 也就是说,您必须指定两个关系,每个关系都是另一个关系。您不能只在一个方向上定义多对多关系并尝试使用它作为多对多。如果你这样做,你将最终得到参照完整性问题。“

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

例:

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

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

结果数据库:
ZDEPARTMENT:
Z_PK
Z_ENT
Z_OPT
Z2DEPARTMENTS(int)
ZNAME

ZEMPLOYEE:
Z_PK
Z_ENT
Z_OPT
ZNAME

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

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

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

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

希望这有点意义:)

首先回复你的评论:

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

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

现在,给出的例子是:

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

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

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

从另一个天使(负面certificate)看它:
假设我们想要反对框架并定义一个没有逆的MANY-to-many关系。
==>
这意味着我们只会在一个方向上实现它,留下一个……多对多的关系或……很多关系
==>
这是同样的事情不是它(从实体1到实体2的多对多关系)
==>
现在,如果我们有一对多的关系并且我们选择不实现它的逆,我们可以选择实现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

您可以在Department的prepareForDeletion方法中强制执行此关系(未测试):
(您将首先在Department上设置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"]; } } }]; } } 

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

问候,
担。

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

如果您有一个从现有部门列表(选择列表等)设置属性的UI,您只需从该列表中获取主键并将其指定为Employee上的departmentID属性。

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

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

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

关于属性validation的Apple文档覆盖-prepareForDeletion-validateValue:forKey:error如果您不熟悉它们会-validateValue:forKey:error