如何防止 NSManagedObject 在覆盖合并时与 CoreData 中的 Swift 重复?
How to prevent to-many relationship NSManagedObject duplicates on overwrite merge with Swift in CoreData?
我的Swift应用程序以JSON的形式从网上API下载了3个数据库,然后将JSON对象转换为CoreData对象,所以应用程序可以运行在互联网访问之外。
我有一个实体 Client,它与 Address 类型的实体具有 toMany 关系。地址与实体客户端具有一对一关系。
客户 <-->> 地址
Client to Addresses 关系具有级联删除规则,Address to Client 关系具有无效删除规则。
Client对id属性有唯一性约束,context始终使用NSMergePolicyType.overwriteMergePolicyType.
当一个新的客户端 NSManagedObject 被实例化,上下文被保存,并且找到一个具有相同 ID 的客户端时,新的客户端覆盖旧的,有一个重要的警告 - 由于某种未知的原因,旧的 Address 对象仍然存在,现在链接到新客户端。这会导致每次重新加载 cache/database 时每个地址都有一个新副本。
我有多个实体具有这样的关系和唯一性,它们 运行 到相同的结果 - 对多对象实例的副本。
对于像 Address 这样的对象,没有一个属性可以封装容器中所有其他 Address 对象的唯一性。它必须是所有属性(地址 1、地址 2、城市、州、邮政编码等)的总和,并根据另一个地址的所有属性的总和检查其唯一性。所以我不确定如何通过唯一性约束来实现这一点——据我所知,唯一性约束不能用 if 逻辑扩展。
另一种解决方案是更改策略的合并行为,或创建自定义合并策略,以确保它在替换之前实际删除旧对象(级联到多对多关系对象)新对象。
我对 CoreData 或 objective-c 不够熟悉,无法了解到目前为止我能找到的关于该主题的任何内容。
有没有人对如何 A. 扩展唯一性约束功能,B. 定义合并策略行为,或 C. 以其他方式防止上述地址对象重复提出建议?
编辑:
我怀疑我关于唯一性约束的假设是错误的 - 请参阅我的单独问题 here
好吧,我可以向你保证,无论是 Objective-C 的知识,还是阅读 Apple 关于子类化 NSMergePolicy 的 non-existent 文档,都不会帮助你解决这个问题:)
我在我自己的小演示项目中确认 Core Data 的唯一性约束不会像人们对 Core Data 的级联删除规则所期望的那样发挥作用。正如您报告的那样,在您的情况下,您只是不断获得越来越多的地址对象。
以下代码解决了我的演示项目中地址对象重复的问题。然而,它的复杂性让人想知道放弃 Core Data 的唯一性约束并编写您自己的老式唯一性代码是否会更好。我想这可能会表现更差,但你永远不知道。
当de-duplicating寻址对象时,可以将现有对象保留在持久存储中或创建新对象。如果确实所有属性都相等,那应该无关紧要。以下代码保留现有对象。这具有不增加对象标识符字符串表示中的 "p" 后缀的美观效果。它们仍然是 "p1"、"p2"、"p3" 等
当您创建持久容器时,在 loadPersistentStores()
完成处理程序中,您将自定义合并策略分配给托管对象上下文,如下所示:
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.viewContext.mergePolicy = MyMergePolicy(merge: .overwriteMergePolicyType)
...
})
最后,这是您的自定义合并策略。传递给 resolve(constraintConflicts list:)
的合并冲突中的 Client 对象具有新的 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)")
}
}
}
}
}
}
}
}
请注意,此代码建立在覆盖合并策略的基础上。我不确定特朗普的其中一项政策是否更合适。
我很确定这就是您所需要的。如果我遗漏了什么,请告诉我。
不确定这是否相关,但我不得不处理类似的情况。我也有关联的对象,它们似乎没有任何我想覆盖的独特之处,但如果你考虑一下 - 它们确实有一些独特之处:事实上它们与特定的独特对象相关方法。
所以我这样做的方法是我连接了足够的关于我的 "Address" 对象的数据,并附加了我的 "Client" 的唯一键,这是我的 "Address".[=10 的唯一键=]
我的Swift应用程序以JSON的形式从网上API下载了3个数据库,然后将JSON对象转换为CoreData对象,所以应用程序可以运行在互联网访问之外。
我有一个实体 Client,它与 Address 类型的实体具有 toMany 关系。地址与实体客户端具有一对一关系。
客户 <-->> 地址
Client to Addresses 关系具有级联删除规则,Address to Client 关系具有无效删除规则。
Client对id属性有唯一性约束,context始终使用NSMergePolicyType.overwriteMergePolicyType.
当一个新的客户端 NSManagedObject 被实例化,上下文被保存,并且找到一个具有相同 ID 的客户端时,新的客户端覆盖旧的,有一个重要的警告 - 由于某种未知的原因,旧的 Address 对象仍然存在,现在链接到新客户端。这会导致每次重新加载 cache/database 时每个地址都有一个新副本。
我有多个实体具有这样的关系和唯一性,它们 运行 到相同的结果 - 对多对象实例的副本。
对于像 Address 这样的对象,没有一个属性可以封装容器中所有其他 Address 对象的唯一性。它必须是所有属性(地址 1、地址 2、城市、州、邮政编码等)的总和,并根据另一个地址的所有属性的总和检查其唯一性。所以我不确定如何通过唯一性约束来实现这一点——据我所知,唯一性约束不能用 if 逻辑扩展。
另一种解决方案是更改策略的合并行为,或创建自定义合并策略,以确保它在替换之前实际删除旧对象(级联到多对多关系对象)新对象。
我对 CoreData 或 objective-c 不够熟悉,无法了解到目前为止我能找到的关于该主题的任何内容。
有没有人对如何 A. 扩展唯一性约束功能,B. 定义合并策略行为,或 C. 以其他方式防止上述地址对象重复提出建议?
编辑:
我怀疑我关于唯一性约束的假设是错误的 - 请参阅我的单独问题 here
好吧,我可以向你保证,无论是 Objective-C 的知识,还是阅读 Apple 关于子类化 NSMergePolicy 的 non-existent 文档,都不会帮助你解决这个问题:)
我在我自己的小演示项目中确认 Core Data 的唯一性约束不会像人们对 Core Data 的级联删除规则所期望的那样发挥作用。正如您报告的那样,在您的情况下,您只是不断获得越来越多的地址对象。
以下代码解决了我的演示项目中地址对象重复的问题。然而,它的复杂性让人想知道放弃 Core Data 的唯一性约束并编写您自己的老式唯一性代码是否会更好。我想这可能会表现更差,但你永远不知道。
当de-duplicating寻址对象时,可以将现有对象保留在持久存储中或创建新对象。如果确实所有属性都相等,那应该无关紧要。以下代码保留现有对象。这具有不增加对象标识符字符串表示中的 "p" 后缀的美观效果。它们仍然是 "p1"、"p2"、"p3" 等
当您创建持久容器时,在 loadPersistentStores()
完成处理程序中,您将自定义合并策略分配给托管对象上下文,如下所示:
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.viewContext.mergePolicy = MyMergePolicy(merge: .overwriteMergePolicyType)
...
})
最后,这是您的自定义合并策略。传递给 resolve(constraintConflicts list:)
的合并冲突中的 Client 对象具有新的 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)")
}
}
}
}
}
}
}
}
请注意,此代码建立在覆盖合并策略的基础上。我不确定特朗普的其中一项政策是否更合适。
我很确定这就是您所需要的。如果我遗漏了什么,请告诉我。
不确定这是否相关,但我不得不处理类似的情况。我也有关联的对象,它们似乎没有任何我想覆盖的独特之处,但如果你考虑一下 - 它们确实有一些独特之处:事实上它们与特定的独特对象相关方法。 所以我这样做的方法是我连接了足够的关于我的 "Address" 对象的数据,并附加了我的 "Client" 的唯一键,这是我的 "Address".[=10 的唯一键=]