2013-04-25 17 views
9

Il mio vecchio modello di dati di base ha un campo NSDate, che vorrei passare a NSNumber. Ho letto la documentazione Apple e diverse domande simili su SO e altri blog (vedi riferimenti alla fine del question)Migrazione personalizzata dei dati principali

Ma non importa quello che faccio, continuo a ricevere lo stesso errore:

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

ho solo 2 versioni del modello e ho verificato più e più volte che i modelli di origine e di destinazione sono corretti.

Ho persino scartato tutte le mie modifiche e ricreato un nuovo modello, mappature ed entità (sottoclassi NSManagedObject). Sono rimasto bloccato per quasi 2 giorni e non ho più idea di cosa sto facendo. Qualsiasi suggerimento su ciò che sto facendo male sarà molto apprezzato.

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

mia abitudine NSEntityMigration classe:


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

Alcune referenze:

  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 Hai provato a impostare la preferenza com.apple.CoreData.MigrationDebug su 1? – Willeke

+0

@Willeke Sì, l'ho fatto. Questo non mi dice esplicitamente perché questo Mismatch tra mappature sta accadendo. – Nishant

+0

È molto difficile capire da qui perché si ottiene l'errore. Fai una nuova domanda e spiega cosa hai modificato nel modello dati, cosa hai modificato nel modello di mappatura predefinito, cosa hai fatto nel codice e quale è il problema simile. – Willeke

risposta

1

Ammetto che non capisco la causa dell'errore. Nella mia migrazione ho una politica per entità e sto verificando l'entità prima di utilizzarla. Non sono sicuro se questo extra if vi aiuterà a:

- (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

Grazie per aver provato Olaf. Ho provato quello che hai detto, e ancora non funziona, anche se ho creato politiche di migrazione individuali per ogni entità nel mio schema. – Neo

+0

Sei riuscito a farlo funzionare? –

+0

@Neo, hai già risolto questo problema? –

1

Tutto quello che stai facendo è molto più complicato di quanto deve essere. Puoi fare tutto questo senza affatto migrare il tuo database. È possibile aggiungere un'altra proprietà alla sottoclasse che lo implementa:

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

E 'un po' fastidioso per avere proprietà duplicati (startDate e startDateNumber), ma è molto più semplice e non ha nessuno dei problemi di migrazione.

+0

Questo è solo un esempio del monumentale modello di dati Core che sto usando. Quindi dovrò capire per migrare correttamente il database, usando le migrazioni multi-pass. – Nishant

Problemi correlati