setIncludesSubentities:在 NSFetchRequest 中,跨多个持久存储的实体被破坏

Posted

技术标签:

【中文标题】setIncludesSubentities:在 NSFetchRequest 中,跨多个持久存储的实体被破坏【英文标题】:setIncludesSubentities: in an NSFetchRequest is broken for entities across multiple persistent stores 【发布时间】:2010-05-03 20:49:41 【问题描述】:

没有完全解决这个问题的现有技术: Core Data Migration error message "'Model does not contain configuration 'XYZ'.'"

我已将其范围缩小到一个特定问题。不过,设置需要一分钟。请多多包涵。

问题的要点是,persistentStoreCoordinator(显然)不能保留对象图的一部分,其中 managedObject 被标记为另一个对象的子实体,当它们存储在不同的文件中时。来了……

1) 我有 2 个 xcdatamodel 文件,每个文件都包含一个实体。在运行时,当构建托管对象模型时,我使用 setSubentities: 手动将一个实体定义为另一个实体的子实体。这是因为尚不支持在编辑器中跨多个文件定义子实体。然后我使用 modelByMergingModels 返回完整的模型。

//Works! 
[mainEntity setSubentities:canvasEntities];
NSLog(@"confirm %@ is super for %@", [[[canvasEntities lastObject] superentity] name], [[canvasEntities lastObject] name]);
//Output: "confirm Note is super for Browser"

2) 我修改了 persistentStoreCoordinator 方法,以便它为每个实体设置不同的存储。从技术上讲,它使用配置,并且每个实体都定义了一个且只有一个配置。

//Also works!
for ( NSString *configName in [[HACanvasPluginManager shared].registeredCanvasTypes valueForKey:@"viewControllerClassName"] ) 
storeUrl = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:[configName stringByAppendingPathExtension:@"sqlite"]]];
//NSLog(@"entities for configuration '%@': %@", configName, [[[self managedObjectModel] entitiesForConfiguration:configName] valueForKey:@"name"]);
//Output: "entities for configuration 'HATextCanvasController': (Note)"
//Output: "entities for configuration 'HAWebCanvasController': (Browser)"
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:configName URL:storeUrl options:options error:&error])
//etc

3) 我为父实体设置了一个 fetchRequest,其中包含 setIncludesSubentities: 和 setAffectedStores: 以确保我们同时涵盖了 1) 和 2)。当插入任一实体的对象时,它们都被添加到上下文中,并且它们都由 fetchedResultsController 获取并按预期显示在 tableView 中。

// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setIncludesSubentities:YES]; //NECESSARY to fetch all canvas types
[fetchRequest setSortDescriptors:sortDescriptors];
[fetchRequest setFetchBatchSize:20]; // Set the batch size to a suitable number.
[fetchRequest setAffectedStores:[[managedObjectContext persistentStoreCoordinator] persistentStores]];
[fetchRequest setReturnsObjectsAsFaults:NO];

这是它开始出现异常行为的地方:关闭并重新启动应用程序后,仅获取父实体。

如果我使用 setEntity: 将请求的实体更改为“Note”的实体,则会获取所有注释。如果我将其更改为“浏览器”的实体,则会获取所有浏览器。让我重申一下,在对象首次插入上下文的运行期间,它将出现在列表中。只有在保存并重新启动后,获取请求才能遍历层次结构。

因此,我只能得出结论,问题在于继承的存储。让我们回顾一下原因:

- 两个实体都可以创建、插入到上下文中并查看,因此模型正在运行 - 两个实体都可以通过一个请求来获取,所以继承是有效的 - 我可以确认文件是单独存储的,并且对象正在进入其适当的存储区,因此正在保存 - 使用为请求设置的任一实体启动应用程序都可以,因此可以从商店中检索 - 这也意味着使用请求遍历不同的商店是有效的 - 通过使用单个存储而不是多个存储,问题完全消失,因此创建、存储、获取、查看等工作正常。

这仅留下一个罪魁祸首(在我看来):我使用 setSubentities: 设置的继承仅对会话期间创建的对象有效。

要么存储的对象/实体没有继承信息,要么以编程方式定义的实体继承仅适用于新实例,或两者兼而有之。其中任何一个都是不可接受的。要么是一个错误,要么是我的方式,偏离了方向。

这两天我一直在这个方向上;非常感谢任何见解。当前的解决方法 - 仅使用单个商店 - 完全有效,但如果我从应用程序中删除其中一个模型等,它不会是面向未来的。它也令人难以置信,因为我不明白为什么你如果核心定义(setSubentities:)不起作用,则将拥有所有这些基础架构,用于跨多个存储进行存储并在获取请求中设置受影响的存储。

【问题讨论】:

【参考方案1】:

不幸的是,答案很简单。不支持跨物理不同的文件和/或模型的子实体。在底层数据结构中,Core Data 会将所有子实体扁平化为一个表。因此,如果您有一个 Parent、ChildA、ChildB 和 ChildC,每个都有 4 个属性,那么您最终将得到一个包含 16 列的表。

这就是您尝试的方式不支持子实体的原因。每个模型本身都是一个孤岛,最多只能对其他模型中的对象进行弱引用。

更新

正如我上面解释的那样,这是一个“设计不支持”的问题。不支持您尝试执行的操作,因为它在底层数据结构中的持久化方式。创建子实体并不是您一开始就应该做的很多事情,因为它会扁平化您的数据模型。

实体继承等于对象继承。您的对象可以以您想要的任何方式、形状或形式继承。实体继承应该是非常罕见的,并且有一个非常很好的理由。

解决父子关系问题是使用实体继承的少数原因之一。

尽量避免列重复不是一个很好的理由。

由于您不能跨模型建立父/子关系(并且您确实有多个模型,即使它们被合并到 NSManagedObjectModel 的一个 实例 中),它不太可能是在无法通过类似解决方案解决的模型之间进行实体继承的理由。

【讨论】:

也许这是正确的答案,但这里的术语是模棱两可的。我的应用只有一个 managedObjectModel,但它是在运行时从多个 xcdatamodel 创建的。为什么有所有这些钩子用于读取多个模型并保存到多个持久存储,如果在中间(在应用程序中)它被破坏了?这就是为什么我认为这不是“设计不支持”的问题,而是“遗漏/错误不支持”的问题。我希望的是一种解决方法......【参考方案2】:

@SG

你问:

为什么会有所有这些钩子用于读取多个模型并保存到多个持久存储...

您可以拥有多个模型并将它们保存到不同的持久存储中,因为您想处理 xcdatamodel 的不同部分,例如编辑 EntityA 的属性,影响与 EntityB 的关系,同时对 EntityB 的大量 managedObjects 进行重新排序。

您还可以在读取新对象时在另一个线程中执行更好的 I/O 流,完成这项工作后,您可以将它们与模型的现有数据“合并”。

【讨论】:

以上是关于setIncludesSubentities:在 NSFetchRequest 中,跨多个持久存储的实体被破坏的主要内容,如果未能解决你的问题,请参考以下文章

NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记

秋的潇洒在啥?在啥在啥?

上传的数据在云端的怎么查看,保存在啥位置?

在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?

存储在 plist 中的数据在模拟器中有效,但在设备中无效

如何在保存在 Mongoose (ExpressJS) 之前在模型中格式化数据