2012-04-19 26 views
11

我有一個大的,預加載的數據庫和一個小型用戶數據庫(CoreData SQLite存儲)的iOS項目。先前的問題建議使用配置來控制哪些實體與哪個商店一起使用。我無法正常工作。這是我一直在努力...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]; 

這工作正常,返回適當命名的類別對象,直到我去掉了另外的第二家門店。如果我這樣做,則獲取結果會回到空白。診斷NSLog消息打印完全符合我的預期。每個商店都與正確的配置相關聯,並且每個配置都有適當的實體。

任何人都可以在源代碼中指出我正在工作的多存儲設置,或者對我做錯的線索瞭解我嗎?提前致謝!


解決:該問題的關鍵是兩行標記在所述第一代碼列表錯誤的。我試圖以編程方式創建配置,但似乎不夠。如果您在執行此操作後查詢ManagedObjectModel以進行配置,則確實可以看到列表中的配置,並且正確的實體與這些配置相關聯。但是,似乎還需要做其他事情才能使PersistentStoreCoordinator能夠正確使用它們。在Xcode中創建配置使它們工作。


跟進:有一個額外的障礙。在設置最終的永久存儲協調器之前運行單獨的遷移通道的解決方案在模擬器中非常有效。在實際的設備上,權限更嚴格。如果您嘗試執行遷移,它將失敗,因爲App包中的存儲區是隻讀的。除非您合併模型,否則遷移似乎是必需的。如果您只有一個模型,並且App包中的商店與其兼容,則遷移不是必需的,並且使用Xcode中定義的配置可以進行訪問。

另一個選項可能是在嘗試遷移之前將數據移入Documents目錄。我沒有證實這種方法有效。

+0

確保您正在對應用程序沙箱的用戶文檔目錄(即讀/寫)進行遷移,而不是在應用程序包本身中進行遷移。 – Sunny 2012-04-22 15:42:13

+0

我不想將數據移動到Documents目錄中,因爲我不想將該(靜態)數據備份並計入用戶的iCloud配額。但是,這聽起來像在iOS 5.0.1中有一種方法來指定文件不被備份:http://developer.apple.com/library/ios/#qa/qa1719/_index.html – Aneel 2012-04-22 18:43:10

+2

嗯,你的啓發我和花了幾個小時解決我的問題後,我寫了一篇關於此[全文]的完整文章(http://blog.atwam.com/blog/2012/05/11/multiple-persistent-stores-and-seed-data -with核心數據/)。我想它可以幫助未來的其他人。 – Wam 2012-05-11 23:42:19

回答

5

您是否嘗試過在同一模型(即相同的媽媽)中定義兩種配置?在編輯其中一個數據模型時,您可以通過選擇「編輯器 - >添加配置」輕鬆完成此操作。將UserData和ItemData的實體拖到適當的配置中。這種方式指定的配置是Core Data所尊重的;這不是關於文件/ URL的名稱。完成上述操作後,請簡化上面的_managedObjectModel以在調用時查找單個momd文件/ URL。或者,如果您決定保留兩個單獨的momd文件,請確保您已在其模型定義文件中分別在名爲「UserData」和「ItemData」的配置中實際定義了模型。

我最初的建議是保留一個模型文件。除非有這些配置不能存在於同一個對象模型中的原因,否則將多個文件複雜化是沒有意義的。我認爲將Core Data技術轉化爲做上面提到的事情會很困難。嘗試簡化代碼的建模部分。

+1

感謝您的回覆。我有充分的理由使用兩個獨立的模型。項目數據模型與另一個項目(用於創建/編輯數據集的OS X應用程序)共享。如果可能的話,我希望能夠將兩個模型分開。 – Aneel 2012-04-21 18:23:50

+1

我嘗試了你的建議,它確實有效。 我將用戶數據模型複製到項目數據模型中,並在XCode中創建了兩個配置。我必須創建一個臨時PSC,然後在每個數據存儲上進行不需要配置的輕量級遷移,然後創建另一個PSC併爲每個存儲添加適當的配置。沒有這些步驟,我仍然會遇到錯誤。與他們一起,PSC將每個實體與正確的商店關聯起來。 我認爲這個統一的模型比我的另一個有兩個單獨的MOM/PSC/MOC的解決方案更不雅觀。謝謝!我仍然想用兩種不同的模型來做到這一點。 – Aneel 2012-04-21 18:31:32

+1

好吧,我也試過你建議保持模型分開。這也行得通!看來我的問題的核心是用編程方式用ManagedObjectModel addEntities定義配置:forConfiguration:不起作用。當您查詢MOM的配置時,它們會顯示出來,但實際上並沒有被PSC正確使用。在Xcode中創建配置必須在幕後做更多的配置。 – Aneel 2012-04-21 18:42:02