我创建了 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 的约束吗?有可能修复它吗?
最佳答案
我认为你在 Core Data 中发现了一个 bug :(
我有一个关于 Core Data 的 Unique Constraint 的小演示项目。我确认它按预期工作 except for some exceptions ,总是将所有重复项与 SQLite 存储合并。然后,我粘贴到您的 MockedDatabaseStackController
类中,并将其 persistentContainer.viewContext
与 NSMergeByPropertyObjectTrumpMergePolicy
集一起使用。结果:它似乎在第一个保存 操作中合并了第一组重复项,但之后就没有了。然后我切换回我的核心数据堆栈,只是我将存储类型更改为 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 - NSInMemoryStoreType 类型的核心数据忽略实体的约束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56077716/