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.viewContext
和NSMergeByPropertyObjectTrumpMergePolicy
设置。结果:似乎在第一次 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 类型的核心数据忽略实体的约束的主要内容,如果未能解决你的问题,请参考以下文章
是否有必要调用 NSManagedObjectContext save: 用于内存存储?
从内存存储中删除 Core Data 对象会将它们变成故障,但不会擦除它们