2013-02-26 84 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; 
}