如何在不复制目标 NSManagedObject 的情况下将目标 NSManagedObject 添加到另一个具有反向多对多核心数据关系的对象?

Posted

技术标签:

【中文标题】如何在不复制目标 NSManagedObject 的情况下将目标 NSManagedObject 添加到另一个具有反向多对多核心数据关系的对象?【英文标题】:How to Add target NSManagedObject to another one with an inverse many-to-many Core Data relationship without duplicating target NSManagedObject? 【发布时间】:2016-08-18 11:17:08 【问题描述】:

我在我的一个项目中遇到了一个问题,我花了几十个小时来寻找解决方案,我取得了一些进展,但仍然没有达到我想要达到的理想状态。我现在仍在自己寻找解决方案,但我非常感谢任何人都可以分享任何关于建设性解决方案的见解。

问题:

如何将主列表中的目标 NSManagedObject 添加为一个对象 另一个 NSManagedObject 中的引用 每个 NSManagedObject 之间没有多对多的关系 在主列表中创建重复的目标 NSManagedObject。

注意:

以下示例使用了与完整数据模型图的类比 到我的真实项目。这个类比是我最能描述这个问题的地方 我有,虽然类比中的对象不完全共享相同 实际项目中对象的名称。

我现在拥有的:

成分对象的主列表,每个对象都是唯一的 其他。

配方对象列表,每个对象都希望有不同的 成分对象来定义配方对象。

我想达到什么目标:

成分对象可以多次插入到单个配方对象中,每次插入作为唯一计数,而不是将相同成分视为一个计数。

我不想在 主列表能够向每个添加多个成分对象 配方对象或跨多个配方对象。

我的尝试:

使用核心数据将成分和配方管理为 2 NSManagedObjects。

已在成分管理对象上创建了一个名为“allHostRecipes”的关系属性,并将其设置为与配方管理对象的“多对多”关系

已在配方管理对象上创建了一个名为“allUsedIngredients”的关系属性,并将其设置为与成分管理对象的“多对多”关系。

这两个关系被设置为“逆”。 我有一个带有表格视图的食谱描述视图,其中列出了食谱中包含和将包含的所有成分。 我创建了另一个成分选择表视图,可以在配方描述视图中触发以选择每种成分,这些成分将被添加到配方中。 每次在成分选择表视图中选择一种成分时,我都会在 NSFetchedResultsController 上调用 objectAtIndexPath(_:),该控制器用于从成分的主列表中获取成分表视图,以在其 ManagedObjectContext 中查找选定的成分对象。

然后我将选定的成分管理对象 (SelectedIngredientManagedObject) 传递回配方描述视图,并在 NSFetchedResultsController 上调用 mutableSetValueForKey("allUsedIngredients").addObject(SelectedIngredientManagedObject) 来获取配方中包含的成分目的。

“用于配料主列表中配料表视图的 NSFetchedResultsController”和“用于获取配方对象内包含的配料的 NSFetchedResultsController”是“配方描述的表视图”中的单独实例变量视图”和“成分选择表视图”。但他们引用了相同的 ManagedObjectContext。

我现在得到了什么:

可以将选定的成分管理对象添加到配方中。 但是,如果我多次选择相同的成分,它只会在配方描述视图的表视图中获得一次计数,而不是每次插入时显示多个计数,这不是我想要实现的上述目标。李>

我的问题:

我应该做什么或调整以实现我拥有的功能 上面的描述?

我认为解决问题的方向:

在定义“多对多”时我还应该做什么 核心数据模型中的关系? “to-Many”引用使用 NSSet 是否会导致计数问题? 是否需要创建多个 ManagedObjectContext 来实现所需的功能? 是否应该将选定的成分管理对象克隆为新的成分管理对象?我试过了,它会将重复的成分添加到成分的主列表中。这也不是我想要的。如果我需要克隆它,我怎样才能使它正确?

非常感谢您抽出宝贵时间查看它,我期待您的见解。非常感谢。

问候,

骑士

【问题讨论】:

【参考方案1】:

您需要稍微改造数据:将多对多关系从Recipe 删除到Ingredient,并将其替换为中间实体(很难找到一个好名字,比如说RecipeIngredientDetails)。

创建从RecipeRecipeIngredientDetails 的一对多关系,例如allUsedIngredientDetails,使用逆(一对一)recipe

同样创建从IngredientRecipeIngredientDetails 的一对多关系,例如allHostRecipeDetails,使用逆(一对一)ingredient

这解决了直接多对多关系的问题,其中每个Recipe 只能与每个Ingredient 关联一次。 (你是对的,这部分是由于关系被建模为集合,它不能有重复的成员)。您有两个选择:您可以添加多个 RecipeIngredientDetails 对象,每个对象都与相同的 RecipeIngredient 对相关。每个这样的对象可能代表成分的标准基本数量。请注意,每个Recipe/Ingredient 对不能只有一个对象,并尝试多次将该对象添加到同一个Recipe:给定的Recipe 和给定的RecipeIngredientDetails 对象可以关联最多一次。

但最好给RecipeIngredientDetails 添加一个属性,比如quantity。然后,您只需为每个 Recipe/Ingredient 对提供一个此类对象,并且可以更新 quantity 属性以反映适合该配方的成分数量。

这是Modeling a Relationship Based on Its Semantics 的 CoreData 编程指南部分中提到的方法:

对于这种关系,使用中间(连接)实体。中间实体的一个优点是您还可以使用它向关系添加更多信息。

相当于在 SQL 数据库中添加一个带有外键和附加列的连接表。我不知道有任何更简单的方法可以在 CoreData 中实现您的目标 - 无法直接向关系添加属性。

关于您在 cmets 中提到的排序问题,您在实体中添加了“用于跟踪订单的 Double 类型属性”。如果您只有两个实体和多对多关系,并且您将 order 属性添加到 Ingredient,那么(例如)如果“Flour”是“Bread”的第一个成分,它必须是第一个item 用于它所使用的每个其他配方。在我描述的方法中,您可以将属性添加到中间实体 RecipeIngredientDetails:索引(就像数量一样)取决于 both配方和成分。

但是,对于索引,我应该提到另一个选项:您可以(在数据模型编辑器中)将 Recipe 到 RecipeIngredientDetails 的关系定义为 ordered。生成的属性将是一个有序集合(因此您可以插入、移除或移动项目以实现正确的顺序)。

【讨论】:

谢谢你,pbasdf。我想你给了我一个很好的方向来解决这个问题,而你上面提到的方法类似于在关系数据库中添加一个单独的表,其中添加的表将包含一组键对,可以是主键或主键外键?虽然 Core Data 有更简单的方法来处理它而不是创建一个单独的表?比如操纵关系上的一些属性? 另外,对于您提到的第一个选项,我是否需要复制多个“RecipeIngredientDetails”并将它们添加到任何相关的配方和成分中?或者只是创建它的 1 个实例并让任何人引用它?对于第二个选项,我想知道是否可以使它符合我的目的,即需要在食谱中订购添加的成分。请注意,我通过在实体中添加一个 Double 类型属性来跟踪顺序,从而解决了 Core Data 模型中的索引问题。那么除了添加数量属性之外还需要什么其他步骤可以帮助解决原始问题吗? 嗨,pbasdf,您提到的方法是Core Data Programming Guide: Creating Managed Object Relationships 中描述为“基于其语义建模关系”部分的方法吗? @JingyuanKnightZhang 我已经更新了我的答案,并提供了更多细节来解决您的问题。 我使用了你提到的方法,这部分项目的工作方式与我最初预期的一样。我还发现有必要将 NSFetchedResultsController 的 fetchRequest 从成分更新到中间实体,让托管表视图在配方中显示新添加的成分。我还在中间实体中创建了一个 Double 类型的属性,并根据 NSDate() 设置它的值来订购配方中的成分。我不确定关系的索引属性是否有帮助。虽然我对当前的解决方案感到非常高兴。非常感谢!

以上是关于如何在不复制目标 NSManagedObject 的情况下将目标 NSManagedObject 添加到另一个具有反向多对多核心数据关系的对象?的主要内容,如果未能解决你的问题,请参考以下文章

获取 NSManagedObject 的属性作为 NSDictionary

核心数据:没有目标实体的关系?

如何在 Core Data 中制作 NSManagedObject 的深拷贝

如何在不使用outerHTML的情况下在javascript中将标签作为文本? [复制]

原因:'一个'NSManagedObject'类的NSManagedObject必须有一个有效的NSEntityDescription? [复制]

是不是不能复制一个核心数据NSManagedObject?