【问题标题】:Core data custom migration核心数据自定义迁移
【发布时间】:2013-04-19 18:54:44
【问题描述】:

我的旧核心数据模型有一个NSDate 字段,我想将其更改为NSNumber。我在 SO 和其他博客上阅读了 Apple 文档和几个类似的问题(请参阅问题末尾的参考资料)

但无论我做什么,我都会收到同样的错误:

由于未捕获的异常“NSInvalidArgumentException”而终止应用,原因:“映射与源/目标模型不匹配”

我只有两个版本的模型,而且我已经反复验证源模型和目标模型是正确的。

我什至放弃了所有更改并重新创建了一个新模型、映射和实体(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/

【问题讨论】:

  • @Nishant 您是否尝试将 com.apple.CoreData.MigrationDebug 首选项设置为 1?
  • @Willeke 是的,我做到了。这并没有明确告诉我为什么会发生映射之间的这种不匹配。
  • 很难从这里判断为什么会出现错误。提出一个新问题,并说明您对数据模型进行了哪些更改,对默认映射模型进行了哪些更改,您在代码中做了什么以及类似的问题是什么。
  • @Nishant。我注意到在您的“createDestinationInstanceFromSourceInstance”方法中,您创建了一个类型为 n 的新托管对象,其中 n 可能是实体映射对象给定的任何类型。然后,您假设该实体类型的 newObject 具有属性“startDate”、“endDate”、“creationTime”、“timeStamp”。可能是映射仅包含一个目标实体类型,所以这可能不是您的问题(或者您的模型中的每个实体类型都可能具有这些属性),但是如果我尝试设置这些属性,我会在尝试设置这些属性之前进行一些实体类型检查你是吗?
  • @TheBasicMind 如果我根本不实现“createDestinationInstancesForSourceInstance”怎么办。我只想要多个映射来迁移我拥有的大数据。不需要自定义迁移。什么是正确的方法?

标签: ios objective-c core-data core-data-migration


【解决方案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;

}

【讨论】:

  • 感谢您尝试奥拉夫。我尝试了你所说的,但它仍然不起作用,即使我为我的架构中的每个实体创建了单独的迁移策略。
  • @Neo,你解决了吗?
【解决方案2】:

你所做的一切都比它必须的要复杂得多。您可以在完全不迁移数据库的情况下完成所有这些操作。您可以向实现它的子类添加另一个属性:

///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)有点烦人,但它要简单得多,而且没有任何迁移问题。

【讨论】:

  • 这只是我正在使用的重要核心数据模型的一个示例。所以我必须弄清楚使用多通道迁移来正确迁移数据库。
猜你喜欢
  • 2015-11-10
  • 2011-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-05
  • 2011-07-22
相关资源
最近更新 更多