2012-06-07 8 views
16

Mam problem z wykonaniem niewielkiej migracji podczas migracji ze sklepu zdefiniowanego przez dwa oddzielne pliki xcdatamodel.Dane podstawowe - migracje lekkie i wielordzeniowe pliki modeli danych (xcdatamodel)

W wersji 1.0 mojej aplikacji miałem modele podzielone na model analityczny, model A i wszystko inne w modelu B. Podczas kompilacji modele były pogrupowane i wszystko przebiegało sprawnie.

Podczas pracy nad nową wersją 1.1 zaktualizowałem model B, dodając nową wersję modelu do modelu B i ustawiając tę ​​nową wersję jako aktywną.

Problem pojawia się podczas aktualizacji z wersji 1.0 do wersji 1.1. Wygląda na to, że Core Data sprawdza skład modelu na dysku (utworzony przez wersję 1.0) i szuka modelu, który go opisuje, ale nie może znaleźć modelu POJEDYNCZEGO, który definiuje cały sklep (model A obejmuje tylko analitykę i obejmuje model B) wszystko inne), więc generuje błąd "Nie można znaleźć modelu dla magazynu źródłowego".

Czy ktoś znalazł rozwiązanie pozwalające oddzielić modele, ale nadal pozwala na uaktualnienia i lekkie migracje do pracy bez dodatkowych problemów związanych z definiowaniem migracji niestandardowych?

Oto fragment kodu używany do ładowania modeli:

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(); 
    } 

Odpowiedz

8

Po uczestnictwie w laboratorium WWDC 2012 i spotkaniu z zespołem Core Data wydaje się, że jesteś zmuszony umieścić wszystkie informacje o swoim modelu w jednym xcdatamodelu. CoreData nie jest wystarczająco inteligentny, aby sprawdzić istniejące sklepy jako kombinację sklepów, które je utworzyły i nadal znajdują się na dysku. Jak zauważył C. Roald, możesz wykonać pewne przetwarzanie na starych plikach xcdatamodel, ale to smutne, że Core Data nie radzi sobie z tym bardziej elegancko.

+0

Jakieś aktualizacje w tej sprawie? – codepushr

+0

@codingrogue - niestety nie. Zespół, nad którym pracowałem, porzuciłem przy użyciu Core Data i przełączyłem się na programowanie Android. Przepraszam. :/ – Mark

0

Najlepszym sposobem, aby uaktualnić swój model Core danych jest dodanie wersji. Jeśli tego nie zrobisz, zginiesz w wypadkach, zaktualizujesz zagrożenia i takie rzeczy.

Dodanie nowej wersji jest całkiem proste. Wybierz plik modelu danych i wybierz "Edytor> Dodaj wersję modelu". Umożliwi to dodanie nowej wersji bazy danych na podstawie poprzedniego modelu. Następnie należy ustawić aktualny model danych do najnowszego: http://cl.ly/2h1g301b0N143t0b1k2K

iOs przeprowadzi migrację danych automatycznie (przynajmniej w moim przypadku) po zainstalowaniu nowej wersji.

mam nadzieję, że to pomoże.

+3

to działa dobrze, gdy masz pojedynczy xcdatamodel. Jeśli masz wiele i uaktualnisz jedną z nich, Core Data nie może się z tym pogodzić. Po uczestnictwie w laboratorium WWDC 2012, wydaje się, że zasadniczo jesteś zmuszony umieścić wszystkie informacje o swoim modelu w jednym xcdatamodelu i zrobić to, co sugerujesz! – Mark

8

Napotkałem ten problem również. Straciłem kilka godzin próbując rozgryźć WTF - bardzo frustrujące.

Wierzę, najprostszym sposobem rozwiązania tego problemu jest:

  1. Picka, który model Trzymamy - mówią ModelB - i utworzyć nową wersję dla niego na podstawie opublikowanej wersji. Zadzwonię do opublikowanej wersji ModelBv1 i nowej wersji ModelBv1_merge.

  2. Otwórz zawartość plików XML dla ModelAv1 i ModelBv1_merge w edytorze tekstu (np. ModelA.xcdatamodeld/ModelAv1.xcdatamodel/contents i ModelB.xcdatamodeld/ModelBv1_merge.xcdatamodel/contents) i scal XML ręcznie. Schemat jest bardzo prosty - wystarczy skopiować elementy <entity> i połączyć element <elements> (w pliku treści _merge) i gotowe.

  3. Otwórz plik zawartości dla nowego ModelBv2 i ponownie scal zawartość ModelA w nim.

  4. Usuń ModelA z pliku projektu.

Sprawdź w Xcode które wyglądają ModelBv1_merge i ModelBv2 zdrowy i zawiera wszystko, czego się spodziewać (unię stary model A i model B).Buduj i powinieneś zrobić.

(Myślę, że ma to zastrzeżenie "pod warunkiem, że oba pliki zawartości zostały napisane przez tę samą wersję Xcode", ale myślę, że jeśli masz stary plik zawartości, powinno to być wystarczająco łatwe, aby Xcode przepisał je, tworząc gdzieś się gdzieś podziała.)

+0

Hacktastyczny sposób, aby nadal mieć 2 pliki modelu danych (nawet jeśli tylko tymczasowo! Dzięki! :) – Mark

+0

Awesome. Uratowałem mój bekon. Dzięki – Darren

+0

Witam, w ten sposób działa świetnie na symulatorze, ale nie działa na urządzeniu? –

5

Mam scenariusz, w którym mój model aplikacja uzyskuje się łączenie kilku modeli, i udało mi się coś w rodzaju automatycznego lekkiego migracji w następujący sposób:

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

Właśnie zacząłem pisać prawie ten sam kod, zanim przeczytałem twoją odpowiedź. Chociaż jest brzydki, wydaje się, że jest to jedyny sposób na zrobienie tego. –

+0

Wow, dzięki, utknąłem z tym problemem przez wiele godzin. Tylko jedno pytanie, czy zachowujesz wszystkie pliki kopii zapasowej? Zgaduję, że po przeprowadzeniu migracji nie ma nic złego w usuwaniu tych – Leonardo

+0

Wow, to naprawdę uratowało moją dupę. Wielkie dzięki!! Mój specyficzny problem polegał na tym, że nijakieśmy jeden stary xcdatamodeld do ręcznej migracji ze starszej aplikacji i odpowiedniej wersji nowego xcdatamodeld. Sądzę, że Core Data dusił się z tego powodu. –

Powiązane problemy