Ios 核心数据插入验证(validateForInsert)触发太早?

Ios 核心数据插入验证(validateForInsert)触发太早?,ios,swift,xcode,validation,core-data,Ios,Swift,Xcode,Validation,Core Data,我正在尝试将核心数据中的验证设置为一个简单的托管对象结构 有一个建筑和楼层以及两者之间的一对多关系(建筑上有许多楼层) 楼层中的验证器如下所示 // extension of auto generated managed object (Floor) extension Floor { override public func validateForInsert() throws { try super.validateForInsert() if (se

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

有一个
建筑
楼层
以及两者之间的一对多关系(建筑上有许多楼层)

楼层中的验证器如下所示

// 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
,但这太晚了,因为我不想在没有与
建筑的正确关系的情况下存储
楼层

有没有关于如何保存具有所需关系和插入时验证的管理对象的建议


谢谢。

当我在2009年第一次开始使用核心数据时,我遇到了类似的错误警报问题。我的解决方案过去是,现在仍然是:不要在顶级主托管对象上下文中使用核心数据验证。也就是说,不要实现
validateForInsert()
。正如您所看到的,即使核心数据正确地标记了一个真正的警报,它也会在保存过程中发生,现在做任何明智的事情都为时已晚。当用户的工作无法保存时,用户会感到不高兴


您可能希望在子托管对象上下文中使用核心数据的验证。在这种情况下,您通常在视图控制器中,验证错误在用户输入后立即被捕获,并且您对错误的即时表示将对用户有意义。

管理器。后台总是返回一个新的上下文,您的
建筑
楼层
存在于两种不同的环境中

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

您正在调用validateForInsert()?另外,请上传您的楼层代码(上下文:_)方法。我不是在调用
validateFroInsert
。它在
楼层
分机中自动调用。我在上面添加了
扩展
部分,以明确
楼层
是托管对象的扩展。
Floor(context:)
方法是一个标准的核心数据管理对象初始化器。看起来您有同步问题。只有在保存上下文时才会调用
validationForInsert
。它看起来像是以前由其他地方触发的。不确定您所说的同步问题是什么意思。实施是非常基本的。为了更好地理解,我将添加经理。您确定您没有在其他地方调用save吗?如果在楼层对象分配给建筑之前调用save,则可能会出现此问题。实际上,两者都处于相同的上下文中,因为我的初始问题中的代码在初始化后将返回相同的上下文。我在同一上下文中看到两个托管对象。即使我只创建
楼层
而不创建
建筑
,我也会在
manager.save()之前收到验证错误
validateForInsert
会在任何托管对象实例化后立即执行。您能告诉我您在尝试保存()时遇到的错误吗?是的,我得到了一个
save
错误:
致命错误:保存错误parentMissingError(“缺少用于构建的父对象”)
问题是,没有运行
manager.save()
,它就会发生。我找到了它。问题来自于我的
单元测试的
拆卸
,在该测试中,所有内容都被清理并在最后保存。非常感谢你和我一起挖掘。有时你会出现代码盲…再次感谢。这仍然令人困惑,因为测试
tearDown
是在测试之后执行的,但是可能with有一些我不知道的异步行为。当我读到亚当已经解决了这个问题时,我的第一反应是删除了我的这个非答案。但经过深思熟虑,我还是坚持下去。如果避免核心数据的验证,就可以避免在同一运行循环中由多个相关对象调用时经常出现的混淆验证错误,
awakeFromInsert/Fetch
tearDown
willTurnIntoFault
等。