【问题标题】:CoreData with multiple stores: configuration woes具有多个存储的 CoreData:配置问题
【发布时间】:2012-04-30 17:28:09
【问题描述】:

我有一个 iOS 项目,其中包含一个大型预加载数据库和一个小型用户数据库(两个都是 CoreData SQLite 存储)。以前的问题建议使用配置来控制哪些实体与哪个商店一起使用。我很难让它发挥作用。这是我一直在尝试的...

- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) return _managedObjectModel;
    // set up the model for the preloaded data
    NSURL *itemURL = [[NSBundle mainBundle] URLForResource:@"FlagDB" withExtension:@"momd"];
    NSManagedObjectModel *itemModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:itemURL];
    // set up the model for the user data
    NSURL *userDataURL = [[NSBundle mainBundle] URLForResource:@"UserData" withExtension:@"momd"];
    NSManagedObjectModel *userDataModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:userDataURL];
    // merge the models
    _managedObjectModel = [NSManagedObjectModel modelByMergingModels:[NSArray arrayWithObjects:itemModel, userDataModel, nil]];
    // define configurations based on what was in each model
WRONG [_managedObjectModel setEntities:itemModel.entities forConfiguration:@"ItemData"];
WRONG [_managedObjectModel setEntities:userDataModel.entities forConfiguration:@"UserData"];
    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
    // preloaded data is inside the bundle
    NSURL *itemURL = [[[NSBundle mainBundle] bundleURL] URLByAppendingPathComponent:@"FlagDB.sqlite"];
    // user data is in the application directory
    NSURL *userDataURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"UserData.sqlite"];

    NSManagedObjectModel *mom = self.managedObjectModel;
    NSError *error = nil;
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];

    if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:@"ItemData"  URL:itemURL options:nil error:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
    ...

这会因“用于打开商店的模型与用于创建商店的模型不兼容”而中止。检查模型中的哈希值与商店中的哈希值表明它们对于 ItemData 配置中的实体是相同的。

如果我尝试进行轻量级迁移,如下所示:

   NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

   NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
   if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:@"ItemData"  URL:itemURL options:options error:&error])

它因“NSInvalidArgumentException”而失败,原因:“模型不包含配置“ItemData”。我认为这是因为轻量级迁移过程正在创建一个新模型,并且它不包含我的配置。

根据其他线程中的一些建议,我尝试在没有配置的情况下进行轻量级迁移,然后使用配置创建新的协调器。这种工作,但它添加表到我的预加载 .sqlite 文件对应于用户数据实体(不属于那里),并在新创建的用户数据存储中创建预加载的数据表和用户数据表.最终结果是提取失败,似乎是因为他们在错误的商店中寻找。

NSDictionary *migrationOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

// make a temp persistent store coordinator to handle the migration
NSPersistentStoreCoordinator *tempPsc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
// migrate the stores
if (![tempPsc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:itemURL options:migrationOptions error:&error])
{
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}
if (![tempPsc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:userDataURL options:migrationOptions error:&error])
{
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

// make a permanent store coordinator
NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];

NSDictionary *readOnlyOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSReadOnlyPersistentStoreOption, nil];
if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:@"ItemData"  URL:itemURL options:readOnlyOptions error:&error])
{
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
}

/*if (![psc addPersistentStoreWithType:NSSQLiteStoreType configuration:@"UserData" URL:userDataURL options:nil error:&error])
 {
 NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
 abort();
 }*/

后来……

    OSAppDelegate *delegate = [UIApplication sharedApplication].delegate;
    NSManagedObjectContext *context = delegate.managedObjectContext;
    // sanity check
    for (NSPersistentStore *store in context.persistentStoreCoordinator.persistentStores) {
        NSLog(@"store %@ -> %@", store.configurationName, store.URL);
        NSMutableArray *entityNames = [[NSMutableArray alloc] init];
        for (NSEntityDescription *entity in [context.persistentStoreCoordinator.managedObjectModel entitiesForConfiguration:store.configurationName]) {
            [entityNames addObject:entity.name];
        }
        NSLog(@"entities: %@", entityNames);
    }

    NSFetchRequest *categoryFetchRequest = [[NSFetchRequest alloc] init];
    categoryFetchRequest.entity = [NSEntityDescription entityForName:@"Category" inManagedObjectContext:context];
    categoryFetchRequest.predicate = [NSPredicate predicateWithFormat:@"name == %@", categoryName];
    NSError *error = nil;
    Category *category = [[delegate.managedObjectContext executeFetchRequest:categoryFetchRequest error:&error] lastObject];

这工作正常,返回适当命名的类别对象,直到我取消注释添加第二个商店。如果我这样做,则获取结果会返回为空。诊断 NSLog 消息完全符合我的预期。每个商店都与正确的配置相关联,并且每个配置都有相应的实体。

谁能指出我在多个商店设置中工作的源代码,或者提示我做错了什么?提前致谢!


已解决:问题的症结在于第一个代码清单中标记为 WRONG 的两行。我试图以编程方式创建配置,但这似乎还不够。如果您在执行此操作后查询 ManagedObjectModel 的配置,您确实确实在列表中看到了配置,并且正确的实体与这些配置相关联。但是,似乎还需要做一些其他事情才能使 PersistentStoreCoordinator 能够正确使用它们。在 Xcode 中创建配置会使它们工作。


跟进:还有一个额外的障碍。在设置最终的 Persistent Store Coordinator 之前运行单独的迁移通道的解决方案效果很好......在模拟器中。在实际设备上,权限更严格。如果您尝试执行该迁移,则会失败,因为 App 包中的存储是只读的。除非您整合模型,否则迁移似乎是必要的。如果您只有一个模型,并且 App 包中的商店与其兼容,则无需迁移,使用 Xcode 中定义的配置访问即可。

另一个选项可能是在尝试迁移之前将数据移动到 Documents 目录中。我尚未验证该方法是否有效。

【问题讨论】:

  • 确保您是在应用沙箱的用户文档目录中进行迁移——它是读/写的——而不是在应用程序包本身中。
  • 我不想将数据移动到 Documents 目录中,因为我不想备份这些(静态)数据并将其计入用户的 iCloud 配额。但听起来在 iOS 5.0.1 中有一种方法可以指定不备份的文件:developer.apple.com/library/ios/#qa/qa1719/_index.html
  • 嗯,你启发了我,在花了几个小时解决我的问题后,我写了一篇关于 here 的完整文章。我认为它可以在未来帮助其他人。
  • 看起来不错。您可能会提到的一件事是,如果您的“种子数据”随捆绑包中的应用程序一起提供,则迁移方法不起作用,因为您无法迁移只读存储。
  • 没错,这就是为什么我小心地将它从捆绑包移动到文档目录的原因。今晚将改变它,将它移动到缓存目录,这样它就不会被 iCloud 备份。我会确保添加此评论以使其更加明显。

标签: objective-c ios core-data ios5 multiple-databases


【解决方案1】:

您是否尝试过在同一个模型(即同一个 momd)中定义这两种配置?您可以通过在编辑其中一个数据模型时选择“编辑器->添加配置”轻松完成此操作。将 UserData 和 ItemData 的实体拖到适当的配置中。以这种方式指定的配置是 Core Data 所尊重的;这与文件/URL 名称无关。完成上述操作后,请简化上面的 _managedObjectModel,以便在调用时查找单个 momd 文件/URL。

或者,如果您决定保留两个单独的 momd 文件,请确保您已在模型定义文件中分别名为“UserData”和“ItemData”的配置中实际定义了模型。

我最初的建议是保留一个模型文件。除非有这些配置不能驻留在同一个对象模型中的原因,否则使用多个文件使事情复杂化是没有意义的。我认为将 Core Data 巧妙地用于执行您在上面尝试执行的操作将非常困难。尝试简化代码的建模部分。

【讨论】:

  • 感谢您的回复。我确实有充分的理由使用两个独立的模型。项目数据模型与另一个项目(用于创建/编辑数据集的 OS X 应用程序)共享。如果可能的话,我希望能够将这两个模型分开。
  • 我尝试了你的建议,它确实有效。我将用户数据模型复制到项目数据模型中,并在 XCode 中创建了两个配置。我必须创建一个临时 PSC 并在每个数据存储上没有配置的情况下进行轻量级迁移,然后创建另一个 PSC 并使用正确的配置添加每个存储。如果没有这些步骤,我仍然会出错。通过它们,PSC 将每个实体与正确的商店相关联。我认为统一模型比我拥有两个单独的 MOM/PSC/MOC 的其他解决方案更不优雅。谢谢!我仍然想用两个独立的模型来做到这一点。
  • 好的,我还尝试了您建议的将模型分开的方法。这也有效!我的问题的核心似乎是使用 ManagedObjectModel addEntities:forConfiguration: 以编程方式定义配置不起作用。当您向 MOM 查询其配置时,它们会显示出来,但它们实际上似乎并没有被 PSC 正确使用。在 Xcode 中创建配置必须在幕后做更多的事情。
  • @Aneel 我已按照您的指示尝试在默认配置中添加新实体,但它给了我错误“配置的实体必须已经在模型中”你能帮我吗?我正在加载使用 url 路径默认创建的模型
  • @chetanpanchal,您可能应该提出一个新问题并提供您正在做什么的全部细节
猜你喜欢
  • 1970-01-01
  • 2015-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-02
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多