核心数据迁移问题:“持久存储迁移失败,缺少源管理对象模型。”

问题描述 投票:8回答:5

的背景

  • 包含两个托管对象模型的Cocoa非文档核心数据项目。
  • 模型1保持不变。模型2已更改,因此我想迁移商店。
  • 我在Xcode中通过Design> Data Model> Add Model Version创建了一个新版本。
  • 版本之间的区别是单个关系,从一个关系变为一个关系。
  • 我对模型进行了更改,然后保存了。
  • 我制作了一个新的映射模型,它将旧模型作为源,将新模型作为目标。
  • 我已经确保所有映射模型和数据模型正在编译,并且所有映射模型和数据模型都被复制到我的应用程序包的Res​​ource文件夹中。
  • 我通过在添加持久存储时使用NSMigratePersistentStoresAutomaticallyOption密钥作为[NSNumber numberWithBool:YES]传入字典来启用迁移。
  • 我没有合并捆绑中的所有模型,而是指定了我想要使用的两个模型(模型1和模型2的新版本)并使用modelByMergingModels:合并它们

问题

无论我做什么迁移,我都会收到错误消息:

“持久存储迁移失败,缺少源管理对象模型。”

我试过的

  • 每次构建后都会清理干净。
  • 我已经尝试了各种组合,只有我在资源中迁移的模型,正在编译或两者兼而有之。
  • 由于错误消息暗示它无法找到我的迁移的源模型,因此我尝试在Resources文件夹中编译每个版本的模型。
  • 我已经确定通过切换回我的数据模型的原始版本,我没有做出一个非常基本的错误。该应用运行正常。
  • 我删除了映射模型和模型的新版本,清理后再重新创建。
  • 我尝试在新模型中进行不同的更改 - 删除实体。

我的智慧结束了。

我不禁想到我在某个地方犯了一个很大的错误,我没有看到。有任何想法吗?

objective-c cocoa core-data migration mapping-model
5个回答
10
投票

两种可能性:

  1. 您应用中的源模型与磁盘上的实际存储不匹配。
  2. 您的映射模型与源模型不匹配。

打开Core Data debugging,你应该能够看到Core Data在进行迁移时正在寻找的哈希值。将这些哈希值与磁盘上的商店中的内容进行比较,看看它们是否匹配。同样,调试应该让您看到映射模型中的哈希值,以帮助您匹配所有内容。

如果只是您的映射模型未对齐,您可以告诉它从Xcode中的设计菜单中更新源。如果您缺少磁盘上存储文件的实际源模型,则可以查看版本控制系统或尝试使用自动迁移将该文件迁移到您认为是源的模型。

更新1

更改源模型和目标模型的位置已移至编辑器窗口的底部:


6
投票

我没有合并捆绑中的所有模型,而是指定了我想要使用的两个模型(模型1和模型2的新版本)并使用modelByMergingModels合并它们:

这似乎不对。为何合并模型?您希望使用模型2,从模型1迁移您的商店。

来自NSManagedObjectModel类的引用

modelByMergingModels:

从现有模型数组中创建单个模型。

您不需要对源模型(模型1)执行任何特殊/特定的操作。只要它在您的包中,自动轻量级迁移过程就会发现并使用它。

我建议放弃你在Xcode中创建的映射模型,因为与自动轻量级迁移相比,我有seen terrible performance。您的里程可能会有所不同,我的模型之间的变化与您的不同,但我不会感到惊讶。在捆绑包中使用和不使用您自己的映射模型尝试一些计时。

 /* Inferred mapping */
 NSError *error;
 NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                          [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                          [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,nil];
 NSPersistentStore *migratedStore = [persistentStoreCoordinator addPersistentStoreWithType:nil
                                                                             configuration:nil
                                                                                       URL:self.storeURL
                                                                                   options:options
                                                                                     error:&error];
 migrationWasSuccessful = (migratedStore != nil);

您可以在代码中验证您的源模型是否可用,方法是尝试加载它并验证它不是nil:

NSString *modelDirectoryPath = [[NSBundle mainBundle] pathForResource:@"YourModelName" ofType:@"momd"];
if (modelDirectoryPath == nil) return nil;
NSString *modelPath = [modelDirectoryPath stringByAppendingPathComponent:@"YourModelName"];
NSURL *modelFileURL = [NSURL fileURLWithPath:modelPath];
NSManagedObjectModel *modelOne = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelFileURL];
if (modelOne == nil) {
    NSLog(@"Woops, Xcode lost my source model");
}
else {
    [modelOne release];
}

这假设您的项目中有一个资源“YourModelName.xcdatamodeld”和“YourModelName.xcdatamodel”。


此外,您可以检查该模型是否与现有的迁移前持久性存储兼容:

NSError *error;
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:self.storeURL error:&error];
if (storeMeta == nil) {
    // Unable to read store meta
    return NO;
}
BOOL isCompatible = [modelOne isConfiguration:nil compatibleWithStoreMetadata:storeMeta];

该代码假定您有一个方法-storeURL来指定持久存储的加载位置。


3
投票

在尝试升级现有应用程序的核心数据模型(并迁移旧数据)时,我遇到了第三方框架将数据写入应用程序数据库的情况。我收到此错误,“无法找到源存储的模型。”由于在尝试迁移时未加载第三方模型,因此迁移失败。

我在解决此问题时编写了此方法(如下所示)。对于那些面临这些类型问题的人来说,这可能是有用的。

- (BOOL) checkModelCompatibilityOfStoreWithURL: (NSURL *) myStoreURL
                                 forModelNamed: (NSString *) myModelName
                                  withBasePath: (NSString *) myBasePath;
{
    NSError * error = nil;
    NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:myStoreURL error:&error];
    if (!storeMeta) {
        NSLog(@"Unable to load store metadata from URL: %@; Error = %@", myStoreURL, error);
        return NO;
    }

    NSString * modelPath = [myBasePath stringByAppendingPathComponent: myModelName];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (![fileManager fileExistsAtPath: modelPath]) {
        // uh oh
        NSLog(@"Can't find model.");
        return NO;
    }

    NSURL * modelURL = [NSURL fileURLWithPath: modelPath];
    NSManagedObjectModel * model = [[[NSManagedObjectModel alloc] initWithContentsOfURL: modelURL] autorelease];
    BOOL result = [model isConfiguration: nil compatibleWithStoreMetadata: storeMeta];

    NSLog(@"Tested model, %@, is%@ compatible with Database", modelPath, result ? @"" : @" ~not~");

    return result;
}

此代码段将获取商店的元数据。

NSError *error = nil;
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeUrl error:&error];
NSLog(@"%@", [storeMeta objectForKey: @"NSStoreModelVersionHashes"]);

VersionInfo.plist(存储在已编译的应用程序包中)包含与模型中各种实体关联的哈希值(base64编码)。类似地,数据存储区中的BLOB列(Z_METADATA.Z_PLIST)包含二进制编码的属性列表,该列表具有与数据关联的每个实体的哈希值(也是base64编码的)。

NSManagedObjectModel上的-entitiesByName方法对于转储特定模型中存在的实体和哈希非常有用。


1
投票

我遇到了类似的问题。我使用过+modelByMergeingModels:,但我没有使用Mapping Model。但是,合并模型不适用于轻量级数据迁移。

来自Apple Docs:

要执行自动轻量级迁移,Core Data需要能够在运行时自行查找源和目标托管对象模型。

如果使用+modelByMergeingModels:而不是用于目标模型。但是Core Data无法找到源模型。在旧版本的应用程序中使用+modelByMergeingModels:创建了源模型,Core Data确实尝试合并模型以找出源模型。

我最终做的是我通过编辑模型的XML文件(手动)创建了一个新合并的.xcdatamodeld,将其添加到项目中,从Compile Sources中删除了单独的.xcdatamodelds而不是使用+modelByMergeingModels:使用NSManagedObjectModel-initWithContentsOfURL:和URL新合并模型我可能会创建一个脚本,将来会自动合并模型。


1
投票

当我收到此错误时,我更新了我的核心数据模型,但没有从我的测试手机中清除应用程序实例。这意味着保存到手机核心数据的模型与我在代码中尝试使用的模型不匹配。

我从手机中删除了应用程序并重新构建/成功运行。

© www.soinside.com 2019 - 2024. All rights reserved.