11

我有一个带有大型预加载数据库和小型用户数据库(两个 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”而失败,原因是:“模型不包含配置“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 中创建配置使它们工作。


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

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

4

1 回答 1

6

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

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

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

于 2012-04-21T00:25:57.927 回答