Как предотвратить дублирование отношений NSManagedObject при слиянии перезаписи со Swift в CoreData? - PullRequest
1 голос
/ 24 мая 2019

Приложение My Swift загружает 3 базы данных из онлайн-API в форме JSON, а затем преобразует объекты JSON в объекты CoreData, чтобы приложение могло функционировать вне доступа к Интернету.

У меня есть сущность Client, которая имеет отношение toMany к сущностям типа Address.Адрес имеет отношение «один к одному» с объектом «Клиент».

Клиент <- >> Адрес

Отношение «Клиент-Адреса» имеет правило каскадного удаления, а отношение «Адрес-клиент» имеет нулевое значение.правило удаления.

Клиент имеет ограничение уникальности для атрибута id, и контекст всегда использует NSMergePolicyType.overwriteMergePolicyType.

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

У меня есть несколько сущностей, которые имеют такие же отношения и уникальность, что приводят к одному и тому же результату - дубликаты множества экземпляров объекта.

Для такого объекта, как Address, нет ни одного атрибута, который мог бы инкапсулировать уникальность среди всех других объектов Address в контейнере.Это должна быть сумма всех атрибутов (адрес1, адрес2, город, штат, почтовый индекс и т. Д.), Которые проверяются на уникальность в сравнении с суммой всех атрибутов другого адреса.Поэтому я не уверен, как это сделать с помощью ограничений уникальности - насколько я могу судить, ограничения уникальности не могут быть расширены с помощью логики if.

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

Я недостаточно знаком с CoreData илиЦель-c - следовать всему, что я смог найти по этому вопросу.

Есть ли у кого-нибудь предложения о том, как: A. расширить возможности ограничений уникальности, B. определить поведение политики слияния или C.в противном случае предотвратить дублирование вышеупомянутых адресных объектов?

edit:

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

1 Ответ

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

Что ж, я могу заверить вас, что ни знание Objective-C, ни чтение несуществующей документации Apple по подклассам NSMergePolicy не помогли бы вам понять это:)

Я подтвердил в своей собственной небольшой демонстрацииПо прогнозам, ограничения уникальности Core Data не воспроизводятся так, как можно было бы ожидать с помощью правила каскадного удаления Core Data.Как вы сообщали, в вашем случае вы просто продолжаете получать все больше и больше объектов Address.

Следующий код решает проблему дублированных объектов Address в моем демонстрационном проекте.Однако его сложность заставляет задуматься, не лучше ли отказаться от ограничения уникальности Core Data и вместо этого написать свой собственный уникальный код старой школы.Я полагаю, что это может работать хуже, но вы никогда не знаете.

При дедупликации объектов Address можно сохранить существующие объекты в постоянном хранилище или создать новые.Это не должно иметь значения, если действительно все атрибуты равны.Следующий код сохраняет существующие объекты.Это имеет эстетически приятный эффект: не увеличивайте суффиксы «p» в строковых представлениях идентификатора объекта.Они остаются как "p1", "p2", "p3" и т. Д.

При создании постоянного контейнера в обработчике завершения loadPersistentStores() пользовательская политика слияния назначается контексту управляемого объекта.как это:

container.loadPersistentStores(completionHandler: { (storeDescription, error) in
    container.viewContext.mergePolicy = MyMergePolicy(merge: .overwriteMergePolicyType)
    ...
})

Наконец, вот ваша политика слияния.Объекты Client в конфликтах слияния, переданные в resolve(constraintConflicts list:), имеют свои новые объекты Address.Переопределение удаляет их, а затем вызывает одну из стандартных политик слияния Core Data, которая при желании добавляет существующие объекты Address.

class MyMergePolicy : NSMergePolicy {
    override func resolve(constraintConflicts list: [NSConstraintConflict]) throws {
        for conflict in list {
            for object in conflict.conflictingObjects {
                if let client = object as? Client {
                    if let addresses = client.addresses {
                        for object in addresses {
                            if let address = object as? Address {
                                client.managedObjectContext?.delete(address)
                            }
                        }
                    }
                }
            }
        }

        /* This is kind of like invoking super, except instead of super
          we invoke a singleton in the CoreData framework.  Weird. */
        try NSOverwriteMergePolicy.resolve(constraintConflicts: list)

        /* This section is for development verification only.  Do not ship. */
        for conflict in list {
            for object in conflict.conflictingObjects {
                if let client = object as? Client {
                    print("Final addresses in Client \(client.identifier) \(client.objectID)")
                    if let addresses = client.addresses {
                        for object in addresses {
                            if let address = object as? Address {
                                print("   Address: \(address.city ?? "nil city") \(address.objectID)")
                            }
                        }
                    }
                }
            }
        }

    }
}

Обратите внимание, что этот код основан на политике слияния Overwrite.Я не уверен, что одна из политик Трампа будет более подходящей.

Я уверен, что это все, что вам нужно.Дайте мне знать, если я что-то пропустил.

...