Базовые данные типа NSInMemoryStoreType игнорируют ограничения сущности - PullRequest
2 голосов
/ 10 мая 2019

Я создал макетную версию стека 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)

Я создал ограничения в модели базы данных, например: enter image description here

Но это не работает. Объекты дублируются в базе данных. enter image description here

Обратите внимание, что все отлично работает в основной цели, где я использую этот стек 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 Ответ

1 голос
/ 13 мая 2019

Я думаю, вы нашли ошибку в Core Data: (

У меня есть небольшой демонстрационный проект для Unique Constraint Core Data. Я подтвердил, что он работает как положено за исключением некоторых исключений , всегда объединяющих все дубликаты с хранилищем SQLite. Затем я вставил в ваш класс MockedDatabaseStackController и использовал его persistentContainer.viewContext с набором NSMergeByPropertyObjectTrumpMergePolicy Результат: Кажется, что объединяются первые наборы дубликатовна первой операции save , но ни одной после этого. Затем я переключился на свой стек основных данных, за исключением того, что изменил тип хранилища на NSInMemoryStoreType. Результат: он также не работает, так же, как ваш MockedDatabaseStackController.

База данных SQLite, которая лежит в основе хранилища SQLite Core Data, поддерживает ограничение UNIQUE в его SQL . Я надеюсь, что кто-то может доказать, что я не прав, но, к сожалению,Я подозреваю, что Apple использовала эту функцию SQLite для реализации функции Unique Constraint в Core Data, но не смогла добавить к их документацию тот факт, что она работает только для SQLitestore.

Я отправил это в Apple Bug Reporter: 50725935.

Что касается вашего теста, я думаю, что вы должны изменить его, чтобы создать временное хранилище SQLite.На самом деле есть некоторые функции, которые находятся на другой стороне диаграммы Венна - поддерживаются хранилищем в памяти, но не поддерживаются хранилищем SQLite.Тестирование в хранилище в памяти может оставить дыры в вашем тестовом покрытии.

...