2

我的 Swift 应用程序从在线 API 以 JSON 形式下载 3 个数据库,然后将 JSON 对象转换为 CoreData 对象,因此该应用程序可以在互联网访问之外运行。

我有一个实体客户端,它与地址类型的实体有 toMany 关系。Address 与实体 Client 具有一对一的关系。

客户 <-->> 地址

客户端到地址的关系有一个级联删除规则,地址到客户端的关系有​​一个无效删除规则。

Client 对 id 属性有唯一性约束,上下文总是使用 NSMergePolicyType.overwriteMergePolicyType。

当一个新的客户端 NSManagedObject 被实例化,上下文被保存,并找到一个具有相同 ID 的客户端时,新的客户端会覆盖旧的客户端,有一个很大的警告——由于某种未知的原因,旧的地址对象仍然存在,现在链接到新客户。这会在每次重新加载缓存/数据库时生成每个地址的新副本。

我有多个实体,它们具有这样的关系和唯一性,它们运行到相同的结果中——多个对象实例的重复。

对于像地址这样的对象,没有一个属性可以封装容器中所有其他地址对象之间的唯一性。它必须是所有属性(地址 1、地址 2、城市、州、邮编等)的总和,根据另一个地址的所有属性的总和检查其唯一性。所以我不确定如何通过唯一性约束来实现这一点——据我所知,唯一性约束不能用 if 逻辑来扩展。

另一种解决方案是更改策略的合并行为,或创建自定义合并策略,以确保在将旧对象替换为新对象之前实际删除旧对象(向下级联到一对多关系对象)。

我对CoreData 或objective-c 不够熟悉,无法了解到目前为止我能找到的关于该主题的任何内容。

是否有人对如何 A. 扩展唯一性约束功能、B. 定义合并策略行为或 C. 以其他方式防止上述地址对象重复有什么建议?

编辑:

我怀疑我对唯一性约束的假设是错误的 - 请在此处查看我的单独问题

4

2 回答 2

3

好吧,我可以向您保证,无论是 Objective-C 的知识,还是阅读 Apple 不存在的关于子类化 NSMergePolicy 的文档,都不会帮助您解决这个问题 :)

我在我自己的小演示项目中确认,Core Data 的唯一性约束不会像人们期望的那样使用 Core Data 的级联删除规则。正如您所报告的,在您的情况下,您只是不断获得越来越多的 Address 对象。

以下代码解决了我的演示项目中重复地址对象的问题。然而,它的复杂性让人怀疑,放弃 Core Data 的唯一性约束并编写自己的老式唯一性代码是否会更好。我想这可能会表现得更糟,但你永远不知道。

在对 Address 对象进行重复数据删除时,可以将现有对象保留在持久存储中,也可以创建新对象。没关系,如果确实所有属性都相等。以下代码保留现有对象。这具有不增加对象标识符字符串表示中的“p”后缀的美观效果。它们保持为“p1”、“p2”、“p3”等。

当您创建持久化容器时,在loadPersistentStores()完成处理程序中,您将自定义合并策略分配给托管对象上下文,如下所示:

container.loadPersistentStores(completionHandler: { (storeDescription, error) in
    container.viewContext.mergePolicy = MyMergePolicy(merge: .overwriteMergePolicyType)
    ...
})

最后,这是您的自定义合并策略。合并冲突中的 Client 对象传递给resolve(constraintConflicts list:)它们的新地址对象。覆盖删除这些,然后调用 Core Data 的标准合并策略之一,该策略根据需要附加现有的 Address 对象。

class MyMergePolicy : NSMergePolicy {
    override func resolve(constraintConflicts list: [NSConstraintConflict]) throws {
        for conflict in list {
            for object in conflict.conflictingObjects {
                if let client = object as? Client {
                    if let addresses = client.addresses {
                        for object in addresses {
                            if let address = object as? Address {
                                client.managedObjectContext?.delete(address)
                            }
                        }
                    }
                }
            }
        }

        /* This is kind of like invoking super, except instead of super
          we invoke a singleton in the CoreData framework.  Weird. */
        try NSOverwriteMergePolicy.resolve(constraintConflicts: list)

        /* This section is for development verification only.  Do not ship. */
        for conflict in list {
            for object in conflict.conflictingObjects {
                if let client = object as? Client {
                    print("Final addresses in Client \(client.identifier) \(client.objectID)")
                    if let addresses = client.addresses {
                        for object in addresses {
                            if let address = object as? Address {
                                print("   Address: \(address.city ?? "nil city") \(address.objectID)")
                            }
                        }
                    }
                }
            }
        }

    }
}

请注意,此代码基于 Overwrite 合并策略构建。我不确定特朗普的一项政策是否更合适。

我很确定这就是你所需要的。如果我遗漏了什么,请告诉我。

于 2019-05-25T02:47:58.550 回答
0

不确定这是否相关,但我不得不处理类似的情况。我也有关联的对象,它们似乎没有我想要覆盖的任何独特之处,但如果你考虑一下 - 它们确实有一些独特之处:事实上它们与特定对象中的唯一对象相关方式。所以我这样做的方式是连接足够的关于我的“地址”对象的数据,并为我的“客户”附加了唯一键,这就是我的“地址”的唯一键。

于 2020-03-11T14:56:53.317 回答