2012-04-19 4 views
11

У меня есть проект iOS с большой предустановленной базой данных и небольшой пользовательской базой данных (как с базами данных CoreData SQLite). Предыдущие вопросы предложили использовать конфигурации, чтобы контролировать, какие объекты используются с каким хранилищем. У меня проблемы с работой. Вот что я пытался ...CoreData с несколькими магазинами: проблемы с конфигурацией

- (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.

Если я пытаюсь делать легкую миграцию, например, так: "Модель не содержит конфигурацию„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», почему“. Я предполагаю, что это потому, что новая модель создается легким процессом миграции, и она не содержит мою конфигурацию.

Основываясь на некоторых предложениях в других потоках, я попытался выполнить легкую миграцию без конфигурации, а затем создав нового координатора, используя конфигурацию. Этот вид работ, но он добавляет таблицы в мой предварительно загруженный файл .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 печатают именно то, что я ожидаю. Каждое хранилище связано с правильной конфигурацией, и каждая конфигурация имеет соответствующие объекты.

Может ли кто-нибудь указать мне на исходный код для работы с несколькими настройками хранилища или определить, что я делаю неправильно? Заранее спасибо!


решаемых: Суть проблемы состояла в том, что две линии, отмеченной НЕПРАВИЛЬНО в первом коде листинга. Я пытался создавать конфигурации программно, но это кажется недостаточным. Если после выполнения запроса вы запросите ManagedObjectModel для конфигураций, вы, do, действительно увидите конфигурации в списке, а соответствующие объекты связаны с этими конфигурациями. Однако кажется, что нужно сделать что-то еще, чтобы заставить PersistentStoreCoordinator правильно использовать их. Создание конфигураций в Xcode заставляет их работать.


РАЗВЕЙТЕ: Там есть дополнительная загвоздка. Решение запуска отдельного миграционного прохода перед настройкой окончательного координатора постоянных хранилищ отлично работает ... в симуляторе. На самом устройстве разрешения более строгие. Если вы попытаетесь выполнить эту миграцию, это терпит неудачу, потому что хранилище в комплекте приложений доступно только для чтения. Возможно, миграция необходима, если вы не консолидируете свои модели. Если у вас есть только одна модель, и хранилище в комплекте приложений совместимо с ней, перенос не требуется, и доступ с использованием конфигураций, определенных в Xcode, работает.

Другим вариантом может быть перемещение данных в каталог документов перед попыткой переноса. Я не подтвердил, что этот подход работает.

+0

Убедитесь, что вы выполняете миграцию в каталоге пользовательских документов изолированной программной среды приложения - которая читается/записывается, а не в самом пакете приложений. – Sunny

+0

Я не хотел переместить данные в каталог «Документы», потому что я не хочу, чтобы эти (статические) данные были скопированы и подсчитаны против квоты пользователя iCloud. Но похоже, что в iOS 5.0.1 есть способ назначить файлы для резервного копирования: http://developer.apple.com/library/ios/#qa/qa1719/_index.html – Aneel

+2

Ну, вы вдохновили меня и потратив несколько часов на решение моей проблемы, я написал полную статью об этом [здесь] (http://blog.atwam.com/blog/2012/05/11/multiple-persistent-stores-and-seed-data -с-ядро-данные /). Я полагал, что это может помочь другим людям в будущем. – Wam

ответ

5

Вы пробовали иметь обе конфигурации, определенные в одной и той же модели (т. Е. Такой же momd)? Вы можете сделать это легко, выбрав «Редактор-> Добавить конфигурацию» при редактировании одной из ваших моделей данных. Перетащите объекты для UserData и ItemData в соответствующую конфигурацию. Конфигурация, указанная таким образом, соответствует тому, что относится к Core Data; дело не в имени файла/URL. После того, как вы сделали это, упростите свой _managedObjectModel выше, чтобы искать один файл/URL-адрес мамы каждый раз, когда он вызывается.

В качестве альтернативы, если вы решили сохранить два отдельных файла momd, убедитесь, что вы действительно определили свои модели в конфигурациях с именем «UserData» и «ItemData» соответственно в своих файлах определения модели.

Мое первоначальное предложение - сохранить один файл модели. Если нет причин, чтобы эти конфигурации не могли находиться в одной и той же объектной модели, не имеет смысла усложнять работу несколькими файлами. Я думаю, что было бы сложно усовершенствовать Core Data в том, что вы делаете выше. Попробуйте упростить часть моделирования вашего кода.

+1

Спасибо за ответ. У меня есть веская причина использовать две отдельные модели. Модель Data Data совместно используется другим проектом (приложение OS X, используемое для создания/редактирования набора данных). Если возможно, я хотел бы иметь возможность оставить две модели отдельными. – Aneel

+1

Я попробовал то, что вы предлагаете, и он работает. Я скопировал модель пользовательских данных в модель данных элемента и создал две конфигурации в XCode. Я должен создать временный PSC и сделать легкую миграцию без конфигурации в каждой из хранилищ данных, затем создать другой PSC и добавить каждый магазин с надлежащей конфигурацией. Без этих шагов я все равно получаю ошибки. С ними PSC связывает каждую сущность с правильным хранилищем. Я думаю, что унифицированная модель менее inelegant, чем мое другое решение с двумя отдельными MOM/PSC/MOC. Благодаря! Мне все же хотелось бы сделать это с помощью двух отдельных моделей. – Aneel

+1

Хорошо, я также попробовал то, что вы предлагаете, чтобы держать модели в отдельности. Это тоже работает! Похоже, что основной проблемой моей проблемы было то, что определение программных программ с помощью ManagedObjectModel addEntities: forConfiguration: не работает. Они отображаются, когда вы запрашиваете MOM для своих конфигураций, но они фактически не используются должным образом PSC. Создание конфигураций в Xcode должно делать больше за кулисами. – Aneel

Смежные вопросы