2012-06-07 34 views
16

從兩個單獨的xcdatamodel文件定義的存儲遷移時,執行輕量級遷移時出現問題。核心數據 - 輕量級遷移和多核心數據模型文件(xcdatamodel)

在我的應用程序1.0版中,我將模型分解爲分析模型,模型-A和模型-B中的所有其他模型。編譯時,模型將被分組在一起,一切進展順利。

在新版本1.1上工作時,我通過將新型號版本添加到型號B並將該新版本設置爲活動狀態來升級型號B.

從1.0升級到1.1時出現此問題。看起來,Core Data檢查磁盤上的模型存儲(由版本1.0創建)並查找描述它的模型,但無法找到定義整個商店的SINGLE模型(模型-A僅涵蓋分析,模型-B覆蓋一切),所以它會拋出一個「無法找到源存儲模型」的錯誤。

有沒有人找到一種解決方案來分離模型,但仍然允許升級+輕量級遷移工作,而無需額外的定義自定義遷移的麻煩?

下面是代碼用來加載模型片段:

NSArray *modelNames = [NSArray arrayWithObjects:@"model-A", @"model-B", nil]; 
    NSMutableArray *models = [NSMutableArray array]; 
    for (NSString *name in modelNames) 
    { 
     LogInfo(@"loading model %@", name); 
     NSURL *modelURL = [[NSBundle mainBundle] URLForResource:name withExtension:@"momd"]; 
     NSManagedObjectModel *model = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] autorelease]; 
     [models addObject:model]; 
    } 

    // combine all the separate models into one big one 
    objectModel = [[NSManagedObjectModel modelByMergingModels:models] retain]; 

    NSURL *documentsDirectory = [NSURL fileURLWithPath:[SuperFileManager documentsDirectory] isDirectory:YES]; 
    NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"database.sqlite"]; 
    NSError *error = nil; 

    coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:objectModel]; 
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
                [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, 
                [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, 
                nil]; 

    if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType 
            configuration:nil 
              URL:storeURL 
             options:options 
              error:&error]) 
    { 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    } 

回答

8

參加WWDC 2012實驗室並與Core Data團隊見面後,似乎您不得不將所有模型信息放入單個xcdatamodel中。 CoreData的智能性不足以將其現有商店作爲創建商店的組合並且仍在磁盤上。正如C. Roald指出的那樣,您可以對舊的xcdatamodel文件進​​行一些處理,但是Core Data不能更好地處理這些問題讓人感到非常難過。

+0

這方面的更新? – codepushr

+0

@codingrogue - 不幸的不是。我當時正在研究的團隊放棄使用Core Data,轉而使用Android編程。抱歉。 :/ – Mark

0

升級你的核心數據模型,最好的辦法是增加一個版本。如果你不這樣做,你將在崩潰中滅亡,更新危險和類似的東西。

添加新版本其實很簡單。您選擇數據模型文件並選擇「編輯器>添加模型版本」。這將使您能夠基於以前的模型添加新的數據庫版本。然後您需要將當前數據模型設置爲最新:http://cl.ly/2h1g301b0N143t0b1k2K

當安裝新版本時,iOs將自動遷移數據(至少在我的情況下)。

希望這有助於。

+3

這工作得很好,當你有一個xcdatamodel。如果您有多個並且您升級其中一個,則Core Data無法協調事情。在參加WWDC 2012實驗室之後,您似乎基本上被迫將所有模型信息放入單個xcdatamodel中,並按照您的建議進行操作! – Mark

8

我也遇到過這個問題。我花了幾個小時試圖弄清楚WTF - 非常令人沮喪。

我認爲解決這個問題的最簡單方法是:

  1. 你保持其模型匹克 - 說ModelB - 和創建基於發佈版本爲它的新版本。我將調用發佈的版本ModelBv1和新版本ModelBv1_merge。

  2. 在文本編輯器中打開ModelAv1和ModelBv1_merge的內容XML文件(即ModelA.xcdatamodeld/ModelAv1.xcdatamodel/contentsModelB.xcdatamodeld/ModelBv1_merge.xcdatamodel/contents)並手動合併XML。該架構非常簡單 - 只需複製<entity>元素並將<elements>元素(合併到_merge內容文件中)完成即可。

  3. 打開新的ModelBv2的內容文件並再次將ModelA內容合併到其中。

  4. 從您的項目文件中刪除ModelA。

檢入Xcode,ModelBv1_merge和ModelBv2看起來很健全,並且包含了你期望的所有東西(舊型號A和型號B的聯合)。建立和你應該完成。 (我認爲這提供了一個告誡:「提供的內容文件都是由相同版本的Xcode編寫的」,但我認爲如果你有一箇舊的內容文件,它應該很容易讓Xcode通過製作一個)

+0

Hacktastic的方式仍然有2個數據模型文件(即使只是暫時的!謝謝!:) – Mark

+0

真棒。保存了我的培根。謝謝 – Darren

+0

你好,這種方式在模擬器上工作很好,但它不能在設備上工作? –

5

我在我的應用模型獲得合併多個型號的情景,我想辦法在這樣一種自動輕量級遷移:

NSError* error = nil; 
NSURL *documentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; 
NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:@"db.sqlite"]; 
NSString* storePath = [storeURL path]; 
NSLog(@"Store URL: %@", storeURL); 
if([[NSFileManager defaultManager] fileExistsAtPath:storePath]){ 
    // Load store metadata (this will contain information about the versions of the models this store was created with) 
    NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeURL error:&error]; 
    if(storeMeta){ 
     // Get the current model, merging all the models in the main bundle (in their current version) 
     NSManagedObjectModel* model=[NSManagedObjectModel mergedModelFromBundles:nil]; 
     // If the persistent store is not compatible with such a model (i.e. it was created with a model obtained merging old versions of "submodels"), migrate 
     if(![model isConfiguration:nil compatibleWithStoreMetadata:storeMeta]){ 


      // Load the old model 
      NSManagedObjectModel*oldModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:storeMeta]; 

      // Compute the mapping between old model and new model 
      NSMappingModel* mapping = [NSMappingModel inferredMappingModelForSourceModel:oldModel destinationModel:model error:&error]; 
      if(mapping){ 
       // Backup old store 
       NSURL* storeBackupURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:[NSString stringWithFormat:@"db.sqlite.%@.bck", [NSDate new]]]; 
       BOOL done = [[NSFileManager defaultManager] moveItemAtURL:storeURL toURL:storeBackupURL error:&error]; 
       if(done){ 
        // Apply the mapping 
        NSMigrationManager* migrationManager = [[NSMigrationManager alloc] initWithSourceModel:oldModel destinationModel:model]; 
        BOOL done = [migrationManager migrateStoreFromURL: storeBackupURL 
                   type: NSSQLiteStoreType 
                   options: nil 
                withMappingModel: mapping 
                toDestinationURL: storeURL 
                 destinationType: NSSQLiteStoreType 
                destinationOptions: nil 
                   error: &error]; 
        if(done){ 
         NSLog(@"Store migration successful!!!"); 
        } 
       } 
      } 
     } 
    } 
} 

if(error){ 
    NSLog(@"Migration error: %@", error); 
} 
+0

在閱讀您的回覆之前,我剛剛發現自己寫了幾乎相同的代碼。雖然很醜,但這似乎是唯一的方法。 –

+0

哇,謝謝,我一直堅持這個問題幾個小時。只有一個問題,你是否保留所有的備份文件?我猜一旦遷移完成,刪除那些 – Leonardo

+0

沒有什麼傷害哇,這真的救了我的屁股。感謝一堆!我的具體問題是我們需要一箇舊的xcdatamodeld用於從舊應用程序進行手動遷移,以及一個正確版本的新xcdatamodeld。我猜Core Data對此感到窒息。 –