Базовые данные с двойной вставкой дочерних записей в ассоциации «один ко многим» - PullRequest
0 голосов
/ 08 апреля 2020

У нас есть приложение iOS, которое использует Core Data для сохранения записей, извлеченных из частного веб-API. Один из наших запросов API выбирает список Project записей, каждая из которых имеет несколько связанных Location записей. ObjectMapper используется для десериализации ответа JSON, и у нас есть собственный преобразователь, который назначает вложенные атрибуты Location для ассоциации базовых данных на объекте Project.

Соответствующая часть кода выглядит следующим образом. Он выполняется в обещании PromiseKit (отсюда seal), и мы сначала сохраняем его в фоновом контексте, а затем распространяем в основной контекст, который используется в потоке пользовательского интерфейса.

WNManagedObjectController.backgroundContext.perform {
    let project = Mapper<Project>().map(JSONObject: JSON(json).object)!

    try! WNManagedObjectController.backgroundContext.save()

    WNManagedObjectController.managedContext.performAndWait {
        do {
            try WNManagedObjectController.managedContext.save()
            seal.fulfill(project.objectID)
        } catch {
            seal.reject(error)
        }
    }
}

Проблема, с которой мы столкнулись, заключается в том, что этот процесс вставки сохраняет каждую Location запись в базу данных дважды . Как ни странно, дублированные записи Location не имеют никакой связи с родительской записью Project. То есть, если Location записи ищутся с NSFetchRequest, или если я запускаю запрос к базовой базе данных SQLite, я вижу, что есть две записи для каждого Location, но только project.locations возвращает одну копию каждого Location. Тот же (или очень похожий) процесс, применяемый к другим типам записей с такой же структурой, также приводит к дублированию.

Я уже пробовал несколько вещей, чтобы сузить проблему:

  • Проверено API JSON - без дубликатов.
  • Проверено состояние свойства project.locations непосредственно перед записью Core Data. До сохранения объектов нет повторяющихся записей, что указывает на корректную работу десериализатора и преобразователя пользовательских вложенных атрибутов.
  • Удален блок, который распространяет изменения в контексте управляемого объекта основного потока, если это было в результате чего вставка происходит дважды. Все еще получайте дубликаты только с записью в фоновый контекст.
  • Запустите приложение с установленным com.apple.CoreData.ConcurrencyDebug 1. В этом процессе не возникает исключений, подтверждающих, что это не проблема безопасности потоков.
  • Запустите приложение с установленным com.apple.CoreData.SQLDebug 1. В журналах видно, что Core Data вставляет ровно в два раза больше строк Location, чем ожидалось, в базовую базу данных SQLite.
  • Реализовано ограничение уникальности для объекта. Это решает проблему с точки зрения того, какие данные сохраняются, но все равно выдаст ошибку, если не установлено NSMergePolicy.

Последний элемент в этом списке эффективно решает проблему, но он лечит симптом, а не причину. Целостность данных важна для нашего приложения, и я пытаюсь понять, в чем может быть основная проблема, или другие варианты, которые я мог бы использовать для дальнейшего ее изучения.

Спасибо!

...