具有多个存储的CoreData:配置难题 [英] CoreData with multiple stores: configuration woes

查看:120
本文介绍了具有多个存储的CoreData:配置难题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an iOS project with a large, preloaded database and a small user database (both CoreData SQLite stores). Previous questions have suggested using configurations to control which Entities are used with which store. I'm having trouble getting that to work. Here's what I've been trying...

- (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配置中的实体是相同的。

This aborts with "The model used to open the store is incompatible with the one used to create the store". Checking the hashes in the model against the hashes in the store show that they're identical for the Entities that are in the ItemData configuration.

如果我尝试进行轻量级迁移,像这样:

If I try doing a lightweight migration, like so:

   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''我假设这是因为一个新的模型正在通过轻量级迁移过程创建, t包含我的配置。

It fails with 'NSInvalidArgumentException', reason: 'Model does not contain configuration 'ItemData'.' I assume that's because a new model is being created by the lightweight migration process, and it doesn't contain my configuration.

根据其他线程中的一些建议,我试着做一个轻量级的迁移,没有配置,然后使用配置创建一个新的协调器。这种工作,但它将表添加到对应于用户数据实体(它不属于那里)的预加载的.sqlite文件,并且在新创建的用户数据存储中创建预加载的数据表和用户数据表。

Based on some suggestions in other threads, I've tried doing a lightweight migration without the configuration, and then creating a new coordinator using the configuration. This sort of works, but it adds tables to my preloaded .sqlite file corresponding to the user data entities (which don't belong there), and creates both the preloaded data tables and the user data tables in the newly-created user data store. The end result is that fetches fail, seemingly because they're looking in the wrong store.

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消息打印完全符合我的期望。每个商店都与正确的配置相关联,并且每个配置都有相应的实体。

This works fine, returning the appropriately named Category object, until I uncomment the addition of the second store. If I do that, the fetch result comes back empty. The diagnostic NSLog messages print exactly what I expect. Each store is associated with correct configuration, and each configuration has the appropriate entities.

任何人都可以指定一个工作的多店铺设置的源代码,或者告诉我我做错了什么?提前感谢!

Can anyone point me at source code for a working multiple store setup, or clue me in to what I'm doing wrong? Thanks in advance!

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

SOLVED: The crux of the problem was the two lines marked WRONG in the first code listing. I was attempting to create configurations programmatically, but that seems to be insufficient. If you query the ManagedObjectModel for configurations after doing this, you do indeed see the configurations in the list, and the correct entities are associated with those configurations. However, it seems that something else needs to be done to make the PersistentStoreCoordinator able to properly use those. Creating the configurations in Xcode makes them work.

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

FOLLOW UP: There's an extra snag. The solution of running a separate migration pass before setting up the final Persistent Store Coordinator works great... in the simulator. On an actual device, the permissions are stricter. If you try to do that migration, it fails because the store in the App bundle is read-only. The migration seems to be necessary unless you consolidate your models. If you have only one model, and the store in the App bundle is compatible with it, the migration is not necessary and access using configurations defined in Xcode works.

可能是在尝试迁移之前将数据移动到Documents目录中。

Another option might be to move data into the Documents directory before attempting the migration. I haven't verified that that approach works.

推荐答案

您是否尝试在同一个模型中定义两个配置)?在编辑一个数据模型时,可以通过选择编辑器 - >添加配置来轻松实现。将UserData和ItemData的实体拖动到相应的配置中。以这种方式指定的配置是Core Data尊重的;它不是关于文件/ URL名称。一旦你完成了上面的工作,然后简化你的_managedObjectModel上面,寻找单一的momd文件/ URL每当它被调用。

Have you tried having both configurations defined in the same model (i.e. same momd)? You can do this easily by selecting "Editor->Add Configuration" while editing one of your data models. Drag entities for UserData and ItemData into the appropriate configuration. The configuration specified this way is what Core Data respects; it's not about the file/URL name. Once you've done the above, then simplify your _managedObjectModel above to look for the single momd file/URL whenever it is called.

或者,如果你决定保持两个单独的momd文件,确保你已经在它们的模型定义文件中分别命名为UserData和ItemData的配置中实际定义了模型。

Alternatively, if you do decide to keep two separate momd files, make sure you've actually defined your models in the Configurations named "UserData" and "ItemData" respectively in their model definition files.

我的初始建议是保留一个模型文件。除非有一个原因这些配置不能驻留在同一个对象模型中,使多个文件复杂的事情没有意义。我认为很难使核心数据做上面你做的工作。尝试简化您的代码的建模部分。

My initial suggestion is to keep one model file. Unless there is a reason these configurations cannot reside in the same object model, it doesn't make sense to complicate things with multiple files. I think it would be quite difficult to finesse Core Data into doing what you are bring to do above. Try to simplify the modeling part of your code.

这篇关于具有多个存储的CoreData:配置难题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆