【问题标题】:Core Data insert validation (validateForInsert) fires too early?核心数据插入验证 (validateForInsert) 触发太早?
【发布时间】:2019-03-29 10:22:29
【问题描述】:

我正在尝试将核心数据中的验证设置为简单的托管对象结构。

BuildingFloor 之间存在一对多的关系(在 Building has many Floors 上)。

Floor 中的验证器如下所示

// extension of auto generated managed object (Floor)
extension Floor {
    override public func validateForInsert() throws {
        try super.validateForInsert()
        if (self.building == nil) {
            throw ValidationError.missingBuilding("a floor requires a building")
        }
    }
}

还有我的简单测试经理:

import Foundation
import CoreData

class ObjectNodeManager {

    let persistentContainer: NSPersistentContainer!

    init(container: NSPersistentContainer) {
        self.persistentContainer = container
        self.persistentContainer.viewContext.automaticallyMergesChangesFromParent = true
    }

    lazy var backgroundContext: NSManagedObjectContext = {
        return self.persistentContainer.newBackgroundContext()
    }()

    func insertObjectNode(objectNode: ObjectNode) -> ObjectNode? {
        backgroundContext.insert(objectNode)
        return objectNode
    }

    func save() {
        if backgroundContext.hasChanges {
            do {
                try backgroundContext.save()
            } catch {
                fatalError("save error \(error)")
            }
        }
    }
}

当我尝试创建一个新建筑和楼层的存档时,我遇到了异常。

let building = Building(context: manager.backgroundContext)
let floor = Floor(context: manager.backgroundContext)
floor.building = building // VALIDATION EXCEPTION!

// a basic backgroundContext.save() (persistentContainer) in the manager
manager.save()

使用validationForInsert,我无法插入/保存托管对象。或者,我可以使用validationForUpdate,但这为时已晚,因为我不想在与Building 没有适当关系的情况下存储Floor

有什么建议如何在插入时保存具有所需关系和验证的管理对象?

谢谢。

【问题讨论】:

  • 你在调用 validateForInsert() 吗?另外,请上传您的 Floor(context:_) 方法代码。
  • 我没有打电话给validateFroInsert。它在Floor 扩展名中自动调用。我在上面添加了extension 部分,以明确Floor 是托管对象的扩展。 Floor(context:_) 方法是标准的核心数据托管对象初始化器。
  • 您似乎遇到了同步问题。 validationForInsert 仅在保存上下文时调用。看起来它之前是由其他地方触发的。
  • 不确定同步问题是什么意思。实现是非常基础的。我会添加经理以便更好地理解。
  • 你确定你没有在其他地方调用保存吗?如果在将 Floor 对象分配给建筑物之前调用了保存,则可能会出现此问题。

标签: ios swift xcode validation core-data


【解决方案1】:

manager.background 上下文总是返回一个新的上下文,你的buildingfloor 存在于两个不同的上下文中。

let backgroundContext = manager.backgroundContext
let building = Building(context: backgroundContext)
let floor = Floor(context: backgroundContext)
floor.building = building
manager.save()

【讨论】:

  • 实际上两者都在相同的上下文中,因为我最初问题中的代码在初始化后将返回相同的上下文。我在同一上下文中看到两个托管对象。即使我只创建Floor 而没有Building,我也会在manager.save() 之前收到验证错误。只要实例化任何托管对象,validateForInsert 就会立即执行。
  • 你能告诉我你在尝试 save() 时遇到的错误吗?
  • 是的,我收到了 save 错误:Fatal error: save error parentMissingError("parent missing for Building") 问题是,它在没有运行 manager.save() 的情况下发生。
  • 我找到了。问题来自我的unit testtearDown,最后所有内容都被清理并保存。非常感谢你和我一起挖掘。有时你的代码是盲目的......再次感谢。它仍然令人困惑,因为测试 tearDown 是在测试之后执行的,但也许 with 有一些我不知道的异步行为。至少我知道去哪里找。
【解决方案2】:

当我在 2009 年第一次开始使用 Core Data 时,我遇到了类似的误报问题。我的解决方案过去和现在仍然是:不要在您的顶级main 托管对象上下文中使用 Core Data 的验证。即不实现validateForInsert()。即使 Core Data 确实正确地标记了一个 true 警报,正如您所看到的,它会在保存期间发生,这为时已晚,无法对其进行任何智能处理。当他们的工作无法保存时,用户会很不高兴。

您可能希望在 child 托管对象上下文中使用 Core Data 的验证。在这种情况下,您通常处于视图控制器中,验证错误会在用户输入后立即被捕获,并且您立即呈现的错误对用户来说是有意义的。

【讨论】:

  • 当我读到 Adam 已经解决了这个问题时,我的第一反应是删除我的这个未回答的问题。但经过反思,我坚持下去。如果你避免 Core Data 的验证,你就可以避免混淆验证错误,当awakeFromInsert/FetchtearDownwillTurnIntoFault 等被多个相关对象在同一个运行循环周期中调用时,经常会发生混淆。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-08
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多