2013-04-25 3 views
9

У моей старой модели данных ядра есть поле NSDate, которое я хотел бы изменить на NSNumber. Я прочитал документацию Apple, и несколько подобных вопросов на SO и другие блоги (см ссылки в конце вопроса)Первичная миграция основных данных

Но независимо от того, что я делаю, я получаю ту же ошибку:

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

У меня есть только 2 версии модели, и я снова и снова проверял правильность исходной и целевой моделей.

Я даже отбросил все свои изменения и воссоздал новую модель, сопоставления и объекты (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

+0

@ Виллеке Да, я сделал это. Это явно не говорит мне, почему это несоответствие между сопоставлениями происходит. – Nishant

+0

Очень сложно сказать, почему вы получаете ошибку. Задайте новый вопрос и скажите, что вы изменили на модель данных, что вы изменили на модель сопоставления по умолчанию, что вы сделали в коде и что такое аналогичная проблема. – Willeke

ответ

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

Спасибо, что попробовали Олаф. Я попробовал то, что вы сказали, и он все еще не работает, хотя я создал отдельные политики миграции для каждого объекта в моей схеме. – Neo

+0

Удалось ли вам это сделать? –

+0

@Neo, вы еще это исправили? –

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

Это немного раздражает, чтобы иметь повторяющиеся свойства (startDate и startDateNumber), но это гораздо проще, и не имеет никаких проблем миграции.

+0

Это всего лишь пример монументальной модели данных Core, которую я использую. Поэтому мне придется рассчитывать на правильную миграцию базы данных, используя многопроходные миграции. – Nishant