具有多个商店的CoreData:配置问题

时间:2012-04-19 08:07:43

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

我有一个带有大型预装数据库和小型用户数据库(两个CoreData SQLite商店)的iOS项目。以前的问题建议使用配置来控制哪些实体与哪个商店一起使用。我无法让它工作。这就是我一直在尝试的......

- (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'而失败,原因是:'Model不包含配置'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];

这很好用,返回适当命名的Category对象,直到我取消注释添加第二个商店。如果我这样做,则获取结果将返回空白。诊断NSLog消息正好打印出我期望的内容。每个商店都与正确的配置相关联,每个配置都有适当的实体。

任何人都可以指点我的源代码进行多工作设置,或者让我知道我做错了什么?提前谢谢!


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


关注:还有一个额外的障碍。在设置最终持久存储协调器之前运行单独的迁移过程的解决方案在模拟器中工作得很好。在实际设备上,权限更严格。如果您尝试执行该迁移,则会失败,因为App捆绑包中的商店是只读的。除非您合并模型,否则迁移似乎是必要的。如果您只有一个模型,并且App捆绑中的商店与之兼容,则不需要迁移,并且使用Xcode中定义的配置进行访问。

另一种选择可能是在尝试迁移之前将数据移动到Documents目录中。我还没有证实这种方法有效。

1 个答案:

答案 0 :(得分:5)

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

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

我最初的建议是保留一个模型文件。除非有这些配置不能驻留在同一对象模型中的原因,否则使多个文件复杂化是没有意义的。我认为将核心数据用于完成您上面所做的工作将非常困难。尝试简化代码的建模部分。