2013-04-25 136 views
9

我的舊核心數據模型有一個NSDate字段,我想將其更改爲NSNumber。我讀蘋果文檔和SO幾個類似的問題和其他博客(見問題的最終參考)核心數據自定義遷移

但無論我做什麼,我不斷收到同樣的錯誤:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Mismatch between mapping and source/destination models'

我只有該模型的兩個版本,並且我已經多次驗證源模型和目標模型是正確的。

我甚至放棄了我的所有更改,並重新創建了一個新模型,映射和實體(NSManagedObject子類)。我一直在這個問題上停留了將近2天,並且不知道我在做什麼。任何指針,我做錯了將不勝感激。

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

    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Old.sqlite"]; 

    NSError *error = nil; 
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; 

    NSString *sourceStoreType = NSSQLiteStoreType; 
    NSURL *sourceStoreURL = storeURL; 

    NSURL *destinationStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"New.sqlite"]; 
    NSString *destinationStoreType = NSSQLiteStoreType; 
    NSDictionary *destinationStoreOptions = nil; 

    NSDictionary *sourceMetadata = 
    [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:sourceStoreType 
                   URL:sourceStoreURL 
                  error:&error]; 

    if (sourceMetadata == nil) { 
     NSLog(@"source metadata is nil"); 
    } 

    NSManagedObjectModel *destinationModel = [_persistentStoreCoordinator managedObjectModel]; 
    BOOL pscCompatibile = [destinationModel 
          isConfiguration:nil 
          compatibleWithStoreMetadata:sourceMetadata]; 

    if (pscCompatibile) { 
     // no need to migrate 
     NSLog(@"is compatible"); 
    } else { 
     NSLog(@"is not compatible"); 

     NSManagedObjectModel *sourceModel = 
     [NSManagedObjectModel mergedModelFromBundles:nil 
            forStoreMetadata:sourceMetadata]; 

     if (sourceModel != nil) { 
      NSLog(@"source model is not nil"); 

      NSMigrationManager *migrationManager = 
      [[NSMigrationManager alloc] initWithSourceModel:sourceModel 
              destinationModel:destinationModel]; 

      NSURL *fileURL = [[NSBundle mainBundle] URLForResource:@"MyMigrationMapping" withExtension:@"cdm"]; 
      NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:fileURL]; 

      NSArray *newEntityMappings = [NSArray arrayWithArray:mappingModel.entityMappings]; 
      for (NSEntityMapping *entityMapping in newEntityMappings) { 
       entityMapping.entityMigrationPolicyClassName = NSStringFromClass([ConvertDateToNumberTransformationPolicy class]); 
      } 
      mappingModel.entityMappings = newEntityMappings; 

      BOOL ok = [migrationManager migrateStoreFromURL:sourceStoreURL 
                 type:sourceStoreType 
                options:nil 
              withMappingModel:mappingModel 
              toDestinationURL:destinationStoreURL 
              destinationType:destinationStoreType 
             destinationOptions:nil 
                 error:&error]; 

      if (ok) { 
       storeURL = destinationStoreURL; 
      } 
     } else { 
      NSLog(@"e nil source model"); 
     } 
    } 

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

    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    }  

    return _persistentStoreCoordinator; 
} 

我定製NSEntityMigration類:


- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance 
             entityMapping:(NSEntityMapping *)mapping 
              manager:(NSMigrationManager *)manager 
               error:(NSError **)error 
{ 
    // Create a new object for the model context 
    NSManagedObject *newObject = 
    [NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName] 
            inManagedObjectContext:[manager destinationContext]]; 

    NSArray *arrayOfKeys = @[@"startDate", @"endDate", @"creationTime", @"timeStamp"]; 

    for (NSString *key in arrayOfKeys) { 
     // do our transfer of NSDate to NSNumber 
     NSDate *date = [sInstance valueForKey:key]; 
     NSLog(@"Key: %@, value: %@", key, [date description]); 

     // set the value for our new object 
     [newObject setValue:[NSNumber numberWithDouble:[date timeIntervalSince1970]] forKey:key]; 
    } 

    // do the coupling of old and new 
    [manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping]; 

    return YES; 
} 

一些參考:

  1. Example or explanation of Core Data Migration with multiple passes?
  2. Core Data - Default Migration (Manual)
  3. http://www.preenandprune.com/cocoamondo/?p=468
  4. http://www.timisted.net/blog/archive/core-data-migration/
+0

@Nishant您是否嘗試將com.apple.CoreData.MigrationDebug首選項設置爲1? – Willeke 2017-07-09 14:44:26

+0

@Willeke是的,我做到了。這並沒有明確告訴我爲什麼這種映射之間的不匹配正在發生。 – Nishant 2017-07-10 05:15:10

+0

從這裏很難判斷出爲什麼會出現錯誤。提出一個新的問題,告訴你改變了什麼樣的數據模型,你改變了什麼默認的映射模型,你在代碼中做了什麼以及類似的問題。 – Willeke 2017-07-10 05:53:30

回答

1

我承認我不明白錯誤的原因。在我的遷移中,每個實體都有一個策略,我在使用它之前檢查實體。不知道這額外if將幫助您:

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance 
            entityMapping:(NSEntityMapping *)mapping 
             manager:(NSMigrationManager *)manager 
              error:(NSError **)error { 

    NSEntityDescription *sourceInstanceEntity = [sInstance entity]; 
    if ([[sInstance name] isEqualToString:@"<-name-of-entity>"]) { 
     newObject = [NSEntityDescription insertNewObjectForEntityForName:@"<-name-of-entity>" 
         inManagedObjectContext:[manager destinationContext]]; 
     NSArray *arrayOfKeys = @[@"startDate", @"endDate", @"creationTime", @"timeStamp"]; 

     for (NSString *key in arrayOfKeys) { 
      // do our transfer of NSDate to NSNumber 
      NSDate *date = [sInstance valueForKey:key]; 
      NSLog(@"Key: %@, value: %@", key, [date description]); 

      // set the value for our new object 
      [newObject setValue:[NSNumber numberWithDouble:[date timeIntervalSince1970]] forKey:key]; 
     } 
    } 

// do the coupling of old and new 
[manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping]; 

return YES; 

}

+0

感謝您試用Olaf。儘管我爲我的模式中的每個實體創建了單獨的遷移策略,但我仍嘗試過您所說的,但仍然無效。 – Neo 2013-05-01 03:18:52

+0

你有沒有設法讓這個工作? – 2014-06-11 14:47:21

+0

@Neo,你有沒有解決這個問題呢? – 2015-03-03 18:59:10

1

一切你正在做的是方式複雜得多,它必須是。你可以做所有這些,而無需遷移你的數據庫。您可以在其他屬性添加到您的子類,實現它:

///in your .h 
@property(nonatomic, copy) NSNumber* startDateNumber 
/// in you .m 
-(NSNumber*) startDateNumber{ 
    if (self.startDate) { 
     return @(self.startDate.timeIntervalSince1970); 
    } 
    return nil; 
} 
-(void)setStartDateNumber:(NSNumber*)startDateNumber{ 
    if(startDateNumber){ 
     self.startDate =[NSDate dateWithTimeIntervalSince1970:startDateNumber.doubleValue]; 
    }else{ 
     self.startDate = nil; 
    } 
} 

這是一個有點惱人的有重複的屬性(startDatestartDateNumber),但它是如此簡單得多,並沒有任何的遷移問題。

+0

這只是我使用的巨大核心數據模型的一個例子。所以我將不得不找出正確的遷移數據庫,使用多通道遷移。 – Nishant 2017-07-10 05:30:05