2013-02-26 56 views
9

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

我启用了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: ... 

回答

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映射文件

+0

所以我们不能在Xcode 4.6.2中使用迁移工具,因为它会生成错误的文件? – 2013-07-04 08:06:47

+2

我不知道这是否是解决方案,但它肯定是一种解决方案。使用由Xcode 5 DP4生成的文件为我工作。 – 2013-08-01 17:24:49

+1

对于Xcode 4,请尝试[此答案](http://stackoverflow.com/a/9428260/1402846)。 – Pang 2013-09-22 07:49:34

0

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

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

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

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

+0

我有同样的问题,虽然我已经完全重写映射模型,我仍然无法得到它的工作。无论我尝试什么,迁移都会失败。 – s1m0n 2013-05-08 14:05:08

0

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

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]; 

与下面的代码

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) [email protected]"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)[email protected]"YES":@"NO"); 
     } 
    } 
    else { 
     //TODO: Error to user... 
     NSLog(@"checkForMigration FAIL - No Mapping Model found!"); 
     abort(); 
    } 
    return migrationSuccess; 
} 
1

后futher调查中,我发现我是遇到了同样的问题如此处所述(Core Data migration fails for to-one relationship)。 设置最小值为1,而不是在我的关系中没有最小值,使得Core Data使用我的自定义映射模型。尽管我不确定我是否认为这是Core Data中的一个错误。
但是,这需要我更改原始(1.0)映射模型(用户已经使用过)。 我想出的修复方法是创建一个名为1.5的1.0和2.0之间的新映射模型。与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; 
}