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

Posted

技术标签:

【中文标题】没有找到适合核心数据迁移的映射模型【英文标题】:No suitable mapping model found for core data migration 【发布时间】:2013-02-26 16:27:33 【问题描述】:

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

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

这是(省略的)日志:

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: ...

【问题讨论】:

【参考方案1】:

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 中生成映射文件。

【讨论】:

所以我们不能使用Xcode 4.6.2中的迁移工具,因为它生成了错误的文件?? 我不知道这是否是解决方案,但它肯定是解决方案。使用 Xcode 5 DP4 生成的文件对我有用。【参考方案2】:

在你的 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;

【讨论】:

【参考方案3】:

经过进一步调查,我发现我遇到了与此处提到的相同的问题 (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;

【讨论】:

【参考方案4】:

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

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

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

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

【讨论】:

我遇到了同样的问题,虽然我已经从头开始重写了映射模型,但我仍然无法让它工作。无论我尝试什么,迁移都会失败。【参考方案5】:

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

【讨论】:

以上是关于没有找到适合核心数据迁移的映射模型的主要内容,如果未能解决你的问题,请参考以下文章

核心数据测试迁移映射模型

核心数据模型迁移

具有多个映射模型的复杂核心数据迁移

使用映射模型在核心数据对象模型之间迁移

在非连续版本之间迁移时出现核心数据迁移错误

iCloud、Core Data、迁移和模型映射