Подтвердить что ты не робот

Основные данные - смешивание облегченной и настраиваемой миграции

Итак, у меня есть версия 1 моего приложения Core Data в App Store, и теперь я начал работать над версией 2.

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

Я могу использовать легкую миграцию Core Data для обработки изменений модели, и после выполнения миграции я могу запустить свой собственный код.

Проблема в том, что я не уверен, что произойдет в будущем, когда я буду строить версию 3,4,5...

предположим, что это так:
версия 1 - версия 2 - используйте легкую миграцию
версия 2 - версия 3 - использование пользовательской миграции с отображением модели
версия 3 - версия 4 - снова используйте легкую миграцию

и т.д.

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

4b9b3361

Ответ 1

Два метода написаны ниже:

  1. Мне лично нравится этот метод, так как он просто гениальный ИМО, из деталей моего объяснения вы можете понять, почему я так взволнован этим методом. Этот метод был написан во втором издании Marcus Zarra. Это реализация для автоматического прогрессивного процесса миграции для тяжелого и легкого использования.

  2. Метод, который я придумал для возможной реализации после того, как OP спросил, есть ли способ избежать необходимости использовать модели отображения для каждого обновления миграции

Метод 1 Здесь начинается прогрессивная миграция. Вы создаете модели отображения для каждого обновления версии, которые будут иметь дело между двумя версиями модели. Модель отображения расскажет менеджеру миграции, как сопоставить старую версию хранилища данных с новой версией хранилища данных. Таким образом, модель отображения требует source и destination.

После этого вам потребуется столько картографических моделей, которые будут охватывать все приращения обновления версии. Таким образом, если у вас есть 4 версии, у вас будет 3 модели сопоставления, которые помогут переходить между обновлениями каждой версии. В конце вы получите их с именем: V1toV2MappingModel, V2to3MappingModel, V3toV4MappingModel.


В каждой модели сопоставления вы можете переключать опции, чтобы менеджер миграции знал, как вы хотите мигрировать. Будь то через пользовательскую политику или обычную миграцию копии. Таким образом, вы говорите, что для версий от v1 до v2 вам требуется легкая миграция, просто выберите подходящую модель сопоставления, затем вы используете редактор моделей/сопоставлений для создания желаемых соединений/сопоставлений между старыми атрибутами и новыми атрибутами, и если требуется пользовательская миграция, затем перейдите к правильной модели сопоставления, выберите сопоставление сущности продукта, для которого вы хотите настроить миграцию, затем в инспекторе entitymapping вы увидите, что вы можете применить настраиваемую политику миграции, просто скопируйте имя вашей политики миграции, скажем например, MigrationPolicyV2toV3 поскольку это именно та версия, которую вы хотели настроить.

enter image description here

Таким образом, на изображении выше вы видите слева от имени модели сопоставления, что оно для Версии 1 в Версию 2. Обратите внимание, что сопоставление атрибутов для ProductToProduct сущностей ProductToProduct пусто - посередине. Это потому, что если вы посмотрите справа на изображение в инспекторе entity mapping где написано " Custom Policy, я поместил название моей политики миграции. После этого диспетчер миграции узнает (в процессе миграции), как сопоставить атрибуты и значения из источника в место назначения (версия 1 и версия 2), и он узнает об этом из-за введенной политики миграции. Это также приводит к изменению type значения на пользовательское. давая вам знать, что это будет пользовательская миграция, когда дело доходит до ProductToProduct сущности ProductToProduct.

Затем вы должны определить в своей политике миграции, которая определит, как вы хотите скопировать значения. Здесь вы делаете свои собственные вещи.

enter image description here

Как видно из изображения выше, это пользовательская политика миграции, которую я установил для ProductToProduct сущности ProductToProduct. Вы заметите, что я на самом деле ничего не делаю по собственному усмотрению, все это можно было бы сделать без политики миграции, и этого можно было бы сделать, просто выполнив миграцию копии (облегченную миграцию), отрегулировав несколько значений в entityMapping inspector и entityMapping inspector Значения Attribute mapping в изображении ранее. Я только выполнил все эти пользовательские настройки политики миграции просто как упражнение, чтобы я мог учиться и быть готовым к будущему только в случае, если мне когда-либо понадобилось выполнить тяжелую миграцию. Лучше выучить это сейчас, чем потом, эй;)

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


И для любых пользовательских/тяжеловесных миграций - где в моем случае я использую migration policy чтобы я мог делать некоторые нестандартные кодовые вещи во время миграции с V2 на V3 в моем хранилище данных - тогда вы создаете нечто, называемое "политикой миграции", чтобы Эта модель сопоставления будет соответствовать заданным вами правилам миграции.

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

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

Таким образом, вы можете разместить всех пользователей с любой версией и довести их до скорости в текущем хранилище версий. Поэтому, если пользователь имеет версию 1, он будет рекурсивно переходить с V1 на V2, а затем мигрировать на v3 вплоть до текущей версии. То же самое относится, если у пользователя есть другая версия.

Это означает, что это займет немного больше времени в процессе миграции, но это хороший способ перенести всех ваших пользователей независимо от того, какая у них версия.

Чтобы найти этот код прогрессивной миграции, вам нужно прочитать книгу под названием Core Data 2nd Edition - Data storage and management for iOS, OS X, and iCloud, глава 3 Versioning and Migration подраздел 3.6 Progressive Data Migration (An Academic Exercise) со страниц 54 to 59

Он рассказывает вам код и шаг за шагом рассказывает, как написать метод progressivelyMigrateURL:ofType:toModel:error: метод. После того, как вы написали метод вместе с ним, он скажет вам, как вызвать этот метод при запуске приложения, чтобы ваши пользователи также могли автоматически переносить свои хранилища.

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

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

Метод 2 - Другое решение, хотя и более громоздкое ИМО: вы всегда можете иметь облегченную настройку миграции, имея в виду, что вы, очевидно, можете достичь даже сложных ситуаций с облегченной миграцией. Но в тех случаях, когда для обновления конкретной версии требуется интенсивная миграция, вы всегда можете сделать операторы if, где вы можете проверить текущее хранилище, и если и ТОЛЬКО если текущая версия постоянного хранилища соответствует обновлению, где требуется интенсивная миграция, Затем вы приказываете менеджеру миграции выполнить интенсивную миграцию и следовать модели сопоставления с политикой миграции только для этого экземпляра, а затем возобновить упрощенную миграцию, если требуется выполнить большее количество обновлений версии, чтобы сохранить постоянное хранилище до самой последней версии модели.,

Ответ 2

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

- (NSManagedObjectContext *)managedObjectContext {
    if (__managedObjectContext != nil) {
        return __managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [__managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return __managedObjectContext;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (__persistentStoreCoordinator != nil) {
        return __persistentStoreCoordinator;
    }

    NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"YOURDB.sqlite"];

    NSError *error = nil;
    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        if (error.code == 134100) {
            if ( [[NSFileManager defaultManager] fileExistsAtPath: [storeURL path]] ) {
                NSDictionary *existingPersistentStoreMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType: NSSQLiteStoreType URL: storeURL error: &error];
                if ( !existingPersistentStoreMetadata ) {
                    // Something *really* bad has happened to the persistent store
                    [NSException raise: NSInternalInconsistencyException format: @"Failed to read metadata for persistent store %@: %@", storeURL, error];
                }

                if ( ![[self managedObjectModel] isConfiguration: nil compatibleWithStoreMetadata: existingPersistentStoreMetadata] ) {
                    if (![[NSFileManager defaultManager] removeItemAtURL: storeURL error: &error] ) {
                        NSLog(@"*** Could not delete persistent store, %@", error);
                        abort();
                    } else {
                        [__persistentStoreCoordinator addPersistentStoreWithType: NSSQLiteStoreType configuration: nil URL: storeURL options: nil error: &error];
                    }
                }
            }
        } else {
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }

    return __persistentStoreCoordinator;
}

Ответ 3

Самый простой способ - сохранить старые атрибуты в новой версии и больше не использовать их. если вам нужно сопоставить их с новыми атрибутами, просто выполните сопоставление после облегченного обновления БД. Мир будет сохранять спокойствие. Преимущества: 1. Нет необходимости составлять картографическую модель. 2. продолжайте использовать легкий вес обновления.

Позвольте привести пример: допустим, у нас есть сущность с именем Student с именем атрибута в V1, и мы сопоставим ее с новым атрибутом firstName в V2. и у нас есть метод, как показано ниже, поэтому мы можем вызвать этот метод после регулярного облегченного обновления CoreData, наслаждайтесь!

- (void)migrateStudentRecords {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Student"];
    NSArray *result = [defaultContext executeFetchRequest:fetchRequest error:error];
    if (result && result.count > 0) {
        [result enumerateObjectsUsingBlock:^(Student  *_Nonnull newRecord, NSUInteger idx, BOOL * _Nonnull stop) {
            newRecord.firstName = newRecord.name;
            newRecord.name = nil;
        }];
        if (defaultContext.hasChanges) {
            [defaultContext save:error];
        }
    }
}