找不到适合核心数据迁移的映射模型

时间:2013-02-26 16:27:33

标签: ios core-data migration

我正在尝试执行需要MappingModel的iOS核心数据迁移。由于某种原因,核心数据无法使用映射模型,并且它会回退到自动轻量级迁移。

我启用了MigrationDebug选项以获取更多信息,我看到的内容毫无意义。映射模型的源哈希值和目标哈希值与源和目标ManagedObjectModels相同,忽略顺序。似乎应该使用映射模型,但日志显示“找不到合适的映射模型”。

这是(省略的)日志:

CoreData: annotation: (migration)    will attempt automatic schema migration
CoreData: annotation: (migration) looking for mapping model with 
 source hashes: 
{
    TSBaseEntity = <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>;
    TSBuyer = <91e837d1 3f348913 eff634d6 6fb9b3a6 747e2390 fbdc4ae6 32cc56d6 7582d4a8>;
    ...
}
 destination hashes: {
    TSBaseEntity = <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>;
    TSBuyer = <e316a857 8919c4be eef15387 5c67a21b 67d32919 99ead438 1ff93c05 2e065fcc>;
    ...
}
CoreData: annotation: (migration) checking mapping model at path file://localhost/Users/xandrews/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/0A84951E-21FC-47C0-A1B7-F880ACB672C4/Dev.app/Migrate_0_5_24To_0_5_27.cdm
 source hashes: 
{(
    <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>,
    <91e837d1 3f348913 eff634d6 6fb9b3a6 747e2390 fbdc4ae6 32cc56d6 7582d4a8>,
    ...
)}
 destination hashes: {(
    <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>,
    <e316a857 8919c4be eef15387 5c67a21b 67d32919 99ead438 1ff93c05 2e065fcc>,
    ...
)}
CoreData: annotation: (migration) no suitable mapping model found
CoreData: annotation: (migration) inferring a mapping model between data models with 
 source hashes: ...

5 个答案:

答案 0 :(得分:8)

Xcode 4生成的映射模型不会产生迁移发生所需的正确哈希值。您可以使用以下代码将迁移日志的输出与映射文件的哈希进行比较:

    NSString *mappingModelPath = [[NSBundle mainBundle] pathForResource:@"MappingFile" ofType:@"cdm"];
    NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:mappingModelPath]];

    for (NSEntityMapping *entityMapping in mappingModel.entityMappings) {
        NSLog(@"%@: %@", entityMapping.sourceEntityName, entityMapping.sourceEntityVersionHash);
        NSLog(@"%@: %@", entityMapping.destinationEntityName, entityMapping.destinationEntityVersionHash);
    }

您会看到这些与迁移日志输出中的哈希值不匹配。

解决方法是在Xcode 5中生成映射文件。

答案 1 :(得分:3)

在persistentStoreCoordinator方法中,给出这行代码

 NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption, nil];

如果这没有帮助,那么您需要进行用户实施的迁移。因此,您必须使用源和目标模型创建映射模型。 在那种情况下,

NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:NO],NSInferMappingModelAutomaticallyOption, nil];

使用以下代码

创建douce元数据
if (sourceMetadata) {
        NSString *configuration = nil;
        NSManagedObjectModel *destinationModel = [self.persistentStoreCoordinator managedObjectModel];

        //Our Source 1 is going to be incompatible with the Version 2 Model, our Source 2 won't be...
        BOOL pscCompatible = [destinationModel isConfiguration:configuration compatibleWithStoreMetadata:sourceMetadata];
        NSLog(@"Is the STORE data COMPATIBLE? %@", (pscCompatible==YES) ?@"YES" :@"NO");

        if (pscCompatible == NO) {
            migrationSuccess = [self performMigrationWithSourceMetadata:sourceMetadata toDestinationModel:destinationModel];
        }
    }
    else {
        NSLog(@"checkForMigration FAIL - No Source Metadata! \nERROR: %@", [error localizedDescription]);
    }

并实现以下功能

- (BOOL)performMigrationWithSourceMetadata :(NSDictionary *)sourceMetadata toDestinationModel:(NSManagedObjectModel *)destinationModel
{
    BOOL migrationSuccess = NO;
    //Initialise a Migration Manager...
    NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil
                                                                    forStoreMetadata:sourceMetadata];
    //Perform the migration...
    if (sourceModel) {
        NSMigrationManager *standardMigrationManager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel
                                                                                      destinationModel:destinationModel];
        //Retrieve the appropriate mapping model...
        NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:nil
                                                                forSourceModel:sourceModel
                                                              destinationModel:destinationModel];
        if (mappingModel) {
            NSError *error = nil;
            NSString *storeSourcePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes.sqlite"];
            NSURL *storeSourceUrl = [NSURL fileURLWithPath: storeSourcePath];
            NSString *storeDestPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes2.sqlite"];
            NSURL *storeDestUrl = [NSURL fileURLWithPath:storeDestPath];

            //Pass nil here because we don't want to use any of these options:
            //NSIgnorePersistentStoreVersioningOption, NSMigratePersistentStoresAutomaticallyOption, or NSInferMappingModelAutomaticallyOption
            NSDictionary *sourceStoreOptions = nil;
            NSDictionary *destinationStoreOptions = nil;

            migrationSuccess = [standardMigrationManager migrateStoreFromURL:storeSourceUrl
                                                                        type:NSSQLiteStoreType
                                                                     options:sourceStoreOptions
                                                            withMappingModel:mappingModel
                                                            toDestinationURL:storeDestUrl
                                                             destinationType:NSSQLiteStoreType
                                                          destinationOptions:destinationStoreOptions
                                                                       error:&error];
            NSLog(@"MIGRATION SUCCESSFUL? %@", (migrationSuccess==YES)?@"YES":@"NO");
        }
    }
    else {
        //TODO: Error to user...
        NSLog(@"checkForMigration FAIL - No Mapping Model found!");
        abort();
    }
    return migrationSuccess;
}

答案 2 :(得分:1)

经过进一步的调查后,我发现我遇到了与此处提到的相同的问题(Core Data migration fails for to-one relationship)。 在我的关系中将最小值设置为1而不是最小值,使Core Data使用我的自定义映射模型。虽然我不确定我是否认为这是Core Data中的一个错误 但是,这要求我更改原始(1.0)映射模型(用户已经在使用)。 我提出的修复是在1.0和2.0之间创建一个名为1.5的新映射模型。与1.0相比,1.5中唯一不同的是关系的最小值,即1.5设置为1。 现在,我可以让Core Data执行从1.0到1.5的轻量级迁移,然后执行我自己的自定义迁移,从1.5到2.0。 虽然它可能是一个hacky解决方案,但它的工作完美。我粘贴了处理下面迁移的代码。

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

    NSURL *storeUrl = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Project.sqlite"]];
    NSError *error;

    NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeUrl error:&error];

    if (! [[self managedObjectModel] isConfiguration:nil compatibleWithStoreMetadata:storeMetadata]) {
        // The current store isn't compatible with the model and should be migrated, check the version identifier of the store

        NSManagedObjectModel *sourceManagedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:storeMetadata];

        if ([[sourceManagedObjectModel versionIdentifiers] containsObject:@"Model_1_0"]) {
            // The current version of the store is 1.0, a manual migration to 1.5 is needed in order to migrate finally to 2.0

            NSURL *destinationModelURL = [[NSBundle mainBundle] URLForResource:@"Model.momd/Model_1_5" withExtension:@"mom"];
            NSManagedObjectModel *destinationManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:destinationModelURL];

            NSMigrationManager *migrationManager = [[NSMigrationManager alloc] initWithSourceModel:sourceManagedObjectModel destinationModel:destinationManagedObjectModel];
            NSMappingModel *inferredMappingModel = [NSMappingModel inferredMappingModelForSourceModel:sourceManagedObjectModel destinationModel:destinationManagedObjectModel error:&error];

            NSURL *destinationURL = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Migration.sqlite"]];

            [migrationManager migrateStoreFromURL:storeUrl
                                             type:NSSQLiteStoreType
                                          options:nil
                                 withMappingModel:inferredMappingModel
                                 toDestinationURL:destinationURL
                                  destinationType:NSSQLiteStoreType
                               destinationOptions:nil
                                            error:&error];
            if (error) {
                DLog(@"Failed to migrate store from URL %@ with mapping model %@ to destination URL %@ with error %@", storeUrl, inferredMappingModel, destinationURL, error);
            }

            [[NSFileManager defaultManager] removeItemAtURL:storeUrl error:&error];
            [[NSFileManager defaultManager] moveItemAtURL:destinationURL toURL:storeUrl error:&error];
        }
    }

    NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption: [NSNumber numberWithBool:YES],
                              NSInferMappingModelAutomaticallyOption: [NSNumber numberWithBool:NO] };
    persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];

    if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
        /*Error for store creation should be handled in here*/
        DLog(@"%@", error);
    }

    return persistentStoreCoordinator;
}

答案 3 :(得分:0)

映射模型可能不足以处理迁移。在这种情况下,即使映射模型与源模型和目标模型匹配,也不会加载映射模型。

为迁移编写测试。逐步重做对模型的更改并测试迁移。这样您就会发现阻止映射模型加载的更改。

了解: 重命名属性或创建属性而不指定默认值。 将属性更改为非可选。

在这些情况下,您必须在映射模型中手动指定行为。

答案 4 :(得分:0)

有同样的问题。我删除了一个实体并相应地重命名了关系字段。我首先尝试使用轻量级迁移,因此为关系指定了重命名ID。由于疏忽,我混淆了用于“重命名ID”和“哈希修改器”的字段。一旦纠正,一切都按预期工作。