NSInMemoryStoreType 类型的核心数据忽略实体的约束

Posted

技术标签:

【中文标题】NSInMemoryStoreType 类型的核心数据忽略实体的约束【英文标题】:Core Data of type NSInMemoryStoreType ignores entity's constraints 【发布时间】:2019-05-10 12:26:27 【问题描述】:

我创建了 CoreData 堆栈的模拟版本

import Foundation
import CoreData

@testable import Companion

final class MockedDatabaseStackController: DatabaseStackControllerProtocol 

    let batchRequestsAvailable: Bool = false

    private lazy var managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle(for: type(of: self))])!

    lazy var persistentContainer: NSPersistentContainer = 
        let description = NSPersistentStoreDescription()
        description.type = NSInMemoryStoreType
        description.shouldAddStoreAsynchronously = false

        let container = NSPersistentContainer(
            name: "database",
            managedObjectModel: managedObjectModel
        )
        container.persistentStoreDescriptions = [description]
        container.loadPersistentStores  description, error in
            // Check if the data store is in memory
            precondition( description.type == NSInMemoryStoreType )

            // Check if creating container wrong
            if let error = error 
                fatalError("Create an in-mem coordinator failed \(error)")
            
        

        return container
    ()

    init() 
        NotificationCenter.default
            .addObserver(
                self,
                selector: #selector(didManagedObjectContextSave(notification:)),
                name: .NSManagedObjectContextDidSave,
                object: nil
            )
    

    @objc
    private func didManagedObjectContextSave(notification: Notification) 
        DispatchQueue.main.async  [weak self] in
            self?.persistentContainer.viewContext.mergeChanges(fromContextDidSave: notification)
        
    

我正在使用它来保存一些对象:

private func executeAndSave<T>(_ executionBlock: @escaping ((NSManagedObjectContext) throws -> T)) -> Single<T> 
    let persistentContainer = stackController.persistentContainer

    return Single.create  observer in
        persistentContainer.performBackgroundTask  context in
            do 
                context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy

                jsons.forEach 
                    let mo = type.init(context: context)
                    mo.update(withGatewayResponse: $0)
                
                try context.save()

                DispatchQueue.main.async 
                    observer(.success(result))
                
             catch 
                DispatchQueue.main.async 
                    observer(.error(error))
                
            
        

        return Disposables.create()
    


func save(jsons: [JSON], as type: GatewayObjectDeserializableAndSavable.Type) -> Single<Void> 
    if jsons.isEmpty 
        log.info("(\(type)) Nothing to save.")
        return .just(())
    

    log.info("DatabaseHelper will save \(type)")

    return executeAndSave  context in
        jsons.forEach 
            let mo = type.init(context: context)
            mo.update(withGatewayResponse: $0)
        
    


// Example of usage:
databaseHelper.save(jsons: jsons, as: Herd.self)

我在数据库模型中创建了约束,例如:

但它不起作用。对象在数据库中重复。

请注意,在我使用此 CoreData 堆栈的主要目标中一切正常:

final class DatabaseStackController: DatabaseStackControllerProtocol 

    // singleton
    static let controller = DatabaseStackController()

    private static let kDatabaseName = "database"
    let persistentContainer: NSPersistentContainer = DatabaseStackController.buildDatabaseStack(onComplete: nil)

    let batchRequestsAvailable: Bool = true

    private init() 
        addNSMangedObjectContextObservers()
    

    private static func buildDatabaseStack(onComplete: (() -> Void)?) -> NSPersistentContainer 
        let container = NSPersistentContainer(name: kDatabaseName)
        container.loadPersistentStores  _, error in
            if let error = error as NSError? 
                fatalError("Unresolved error \(error), \(error.userInfo)")
            

            onComplete?()
        

        return container
    

为什么它不起作用? NSInMemoryStoreType 不支持 CoreData 的约束吗?有可能修复它吗?

【问题讨论】:

【参考方案1】:

我认为您在 Core Data 中发现了一个错误 :(

我有一个关于 Core Data 的 Unique Constraint 的小演示项目。我确认它按预期工作except for some exceptions,总是将所有重复项与 SQLite 存储合并。然后我粘贴到你的MockedDatabaseStackController 类中,并使用它的persistentContainer.viewContextNSMergeByPropertyObjectTrumpMergePolicy 设置。结果:似乎在第一次 save 操作中合并了第一组重复项,但之后没有。然后我切换回我的核心数据堆栈,除了我将存储类型更改为NSInMemoryStoreType。结果:它也不能工作,和你的MockedDatabaseStackController一样。

作为 Core Data 的 SQLite 存储基础的 SQLite 数据库支持 its SQL 中的 UNIQUE 约束。我希望有人能证明我错了,但遗憾的是,我怀疑 Apple 使用 SQLite 的这个特性来实现 Core Data 中的 Unique Constraint 特性,但未能向their documentation 添加它的事实仅适用于 SQLite 商店。

我已将此提交给 Apple Bug Reporter:50725935。

至于您的测试,我认为您应该修改它以创建一个临时 SQLite 存储。事实上,维恩图的另一边有一些特性——内存存储支持,但 SQLite 存储不支持。使用内存存储进行测试可能会在测试覆盖率中留下漏洞。

【讨论】:

不幸的是,ios 13 上的情况仍然如此。 Jerry:我怀疑 Apple 的快速实施也是如此,截至 2020 年 12 月,这个错误仍然存​​在。正如您所建议的那样,快速修复是使用临时 SQLite 进行测试。我使用storeType: NSInMemoryStoreType 进行测试以加快执行速度...

以上是关于NSInMemoryStoreType 类型的核心数据忽略实体的约束的主要内容,如果未能解决你的问题,请参考以下文章

NSBinaryStoreType 的优点是啥?

是否有必要调用 NSManagedObjectContext save: 用于内存存储?

如何将 Core Data 用于主应用程序和单元测试?

从内存存储中删除 Core Data 对象会将它们变成故障,但不会擦除它们

Elasticsearch-用于定义文档字段的核心类型-字符串类型

C# 1的核心基础之二——类型系统