17

从由两个单独的 xcdatamodel 文件定义的存储迁移时,我在执行轻量级迁移时遇到问题。

在我的应用程序 1.0 版中,我将模型分解为分析模型、模型 A 以及模型 B 中的所有其他内容。编译时,模型将组合在一起,一切顺利。

在开发新版本 1.1 时,我通过向模型 B 添加新模型版本并将该新版本设置为活动来升级模型 B。

从 1.0 升级到 1.1 时会出现此问题。似乎 Core Data 检查磁盘上的模型存储(由 1.0 版创建)并查找描述它的模型,但无法找到定义整个存储的 SINGLE 模型(模型 A 仅涵盖分析,模型 B 涵盖其他所有内容),因此它会引发“找不到源存储模型”错误。

有没有人找到一种解决方案来分离模型,但仍然允许升级 + 轻量级迁移工作,而无需定义自定义迁移的额外麻烦?

这是用于加载模型的代码片段:

    NSArray *modelNames = [NSArray arrayWithObjects:@"model-A", @"model-B", nil];
    NSMutableArray *models = [NSMutableArray array];
    for (NSString *name in modelNames)
    {
        LogInfo(@"loading model %@", name);
        NSURL *modelURL = [[NSBundle mainBundle] URLForResource:name withExtension:@"momd"];
        NSManagedObjectModel *model = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] autorelease];
        [models addObject:model];
    }

    // combine all the separate models into one big one
    objectModel = [[NSManagedObjectModel modelByMergingModels:models] retain];

    NSURL *documentsDirectory = [NSURL fileURLWithPath:[SuperFileManager documentsDirectory] isDirectory:YES];
    NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"database.sqlite"];
    NSError *error = nil;

    coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:objectModel];
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                                  [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                                  [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,
                                                  nil];

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

4 回答 4

9

在参加了 WWDC 2012 实验室并与 Core Data 团队会面后,您似乎被迫将所有模型信息放在单个 xcdatamodel 中。CoreData 不够智能,无法将其现有存储区作为创建它且仍在磁盘上的存储区的组合进行检查。正如 C. Roald 所指出的,您可以对旧的 xcdatamodel 文件进行一些处理,但很遗憾 Core Data 没有更优雅地处理这个问题。

于 2012-12-13T02:41:09.780 回答
8

我也遇到了这个问题。我花了几个小时试图弄清楚 WTF - 非常令人沮丧。

我相信解决这个问题的最简单方法是:

  1. 选择您要保留的模型——比如 ModelB——并根据已发布的版本为其创建一个新版本。我将发布版本 ModelBv1 和新版本 ModelBv1_merge 称为。

  2. 在文本编辑器(即ModelA.xcdatamodeld/ModelAv1.xcdatamodel/contentsModelB.xcdatamodeld/ModelBv1_merge.xcdatamodel/contents)中打开 ModelAv1 和 ModelBv1_merge 的内容 XML 文件,然后手动合并 XML。架构非常简单——只需复制<entity>元素并合并<elements>元素(到 _merge 内容文件中)就可以了。

  3. 打开新 ModelBv2 的内容文件,然后再次将 ModelA 内容合并到其中。

  4. 从项目文件中删除 ModelA。

在 Xcode 中检查 ModelBv1_merge 和 ModelBv2 看起来很正常,并且包含您期望的所有内容(旧模型 A 和模型 B 的联合)。构建,你应该完成。

(我认为这有一个警告“只要两个内容文件都是由相同版本的 Xcode 编写的”,但我认为如果你有一个旧的内容文件,它应该很容易通过在某处进行微不足道的更改来让 Xcode 重写它。 )

于 2012-12-07T00:11:52.490 回答
5

我有一个场景,我的应用程序模型是通过合并多个模型获得的,并且我设法以这种方式进行了一种自动轻量级迁移:

NSError* error = nil;
NSURL *documentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"db.sqlite"];
NSString* storePath = [storeURL path];
NSLog(@"Store URL: %@", storeURL);
if( [[NSFileManager defaultManager] fileExistsAtPath:storePath] ){
    // Load store metadata (this will contain information about the versions of the models this store was created with)
    NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeURL error:&error];
    if(storeMeta){
        // Get the current model, merging all the models in the main bundle (in their current version)
        NSManagedObjectModel* model=[NSManagedObjectModel mergedModelFromBundles:nil];
        // If the persistent store is not compatible with such a model (i.e. it was created with a model obtained merging old versions of "submodels"), migrate
        if(![model isConfiguration:nil compatibleWithStoreMetadata:storeMeta]){


            // Load the old model
            NSManagedObjectModel*oldModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:storeMeta];

            // Compute the mapping between old model and new model
            NSMappingModel* mapping = [NSMappingModel inferredMappingModelForSourceModel:oldModel destinationModel:model error:&error];
            if(mapping){
                // Backup old store
                NSURL* storeBackupURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:@"db.sqlite.%@.bck", [NSDate new]]];
                BOOL done = [[NSFileManager defaultManager] moveItemAtURL:storeURL toURL:storeBackupURL error:&error];
                if(done){
                    // Apply the mapping
                    NSMigrationManager* migrationManager = [[NSMigrationManager alloc] initWithSourceModel:oldModel destinationModel:model];
                    BOOL done = [migrationManager migrateStoreFromURL: storeBackupURL
                                                                 type: NSSQLiteStoreType
                                                              options: nil
                                                     withMappingModel: mapping
                                                     toDestinationURL: storeURL
                                                      destinationType: NSSQLiteStoreType
                                                   destinationOptions: nil
                                                                error: &error];
                    if(done){
                        NSLog(@"Store migration successful!!!");
                    }
                }
            }
        }
    }
}

if(error){
    NSLog(@"Migration error: %@", error);
}
于 2014-06-10T15:43:40.220 回答
0

升级 Core Data 模型的最佳方式是添加一个版本。如果你不这样做,你将在崩溃、更新危险和类似的事情中丧生。

添加新版本实际上非常容易。您选择数据模型文件并选择“编辑器 > 添加模型版本”。这将使您能够基于以前的模型添加新的数据库版本。然后您需要将当前数据模型设置为最新:http ://cl.ly/2h1g301b0N143t0b1k2K

安装新版本时,iOs 将自动迁移数据(至少在我的情况下)。

希望这可以帮助。

于 2012-06-18T15:22:57.783 回答