2012-04-19 8 views
11

Tengo un proyecto de iOS con una gran base de datos precargada y una pequeña base de datos de usuario (ambas tiendas CoreData SQLite). Las preguntas anteriores sugirieron usar configuraciones para controlar qué entidades se usan con qué tienda. Tengo problemas para conseguir que funcione. Esto es lo que he estado tratando ...CoreData con múltiples almacenes: problemas de configuración

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

Esto aborta con "El modelo utilizado para abrir la tienda es incompatible con la utilizada para crear el almacén". Al verificar los valores hash en el modelo frente a los valores hash en la tienda, se muestra que son idénticos para las entidades que están en la configuración de ItemData.

Si intento hacer una migración de peso ligero, así: 'Modelo no contiene la configuración '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]) 

falla con 'NSInvalidArgumentException', razón''. Supongo que es porque el proceso de migración liviano está creando un nuevo modelo y no contiene mi configuración.

Basado en algunas sugerencias en otros hilos, he intentado hacer una migración liviana sin la configuración, y luego crear un nuevo coordinador usando la configuración. Este tipo de trabajo funciona, pero agrega tablas a mi archivo .sqlite precargado correspondiente a las entidades de datos de usuario (que no pertenecen) y crea las tablas de datos precargados y las tablas de datos de usuario en el almacén de datos de usuario recién creado. . El resultado final es que las recuperaciones fallan, aparentemente porque están buscando en la tienda equivocada.

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

Y más tarde ...

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]; 

Esto funciona bien, volviendo al objeto Categoría apropiado nombre, hasta que elimine el comentario de la adición de la segunda tienda. Si hago eso, el resultado de búsqueda vuelve vacío. Los mensajes de diagnóstico NSLog imprimen exactamente lo que espero. Cada tienda está asociada con la configuración correcta, y cada configuración tiene las entidades apropiadas.

¿Alguien puede indicarme el código fuente para una configuración de varias tiendas en funcionamiento, o darme una pista de lo que estoy haciendo mal? ¡Gracias por adelantado!


RESUELTO: El quid de la cuestión era las dos líneas marcadas mal en el primer listado de código. Estaba intentando crear configuraciones programáticamente, pero eso parece ser insuficiente. Si consulta ManagedObjectModel para obtener configuraciones después de hacer esto, haga vea las configuraciones en la lista, y las entidades correctas estén asociadas con esas configuraciones. Sin embargo, parece que se necesita hacer algo más para que PersistentStoreCoordinator pueda usarlos correctamente. Crear las configuraciones en Xcode hace que funcionen.


SEGUIMIENTO: Hay un inconveniente adicional. La solución de ejecutar un pase de migración por separado antes de configurar el coordinador de tienda persistente final funciona muy bien ... en el simulador. En un dispositivo real, los permisos son más estrictos. Si intenta hacer esa migración, falla porque la tienda en el paquete de la aplicación es de solo lectura. La migración parece ser necesaria a menos que consolide sus modelos. Si solo tiene un modelo y la tienda en el paquete de la Aplicación es compatible con él, la migración no es necesaria y funciona utilizando las configuraciones definidas en Xcode.

Otra opción podría ser mover datos al directorio de Documentos antes de intentar la migración. No he verificado que ese enfoque funcione.

+0

Asegúrese de realizar la migración en el directorio del documento de usuario de la aplicación sandbox, que es de lectura/escritura, y no en el paquete de la aplicación en sí. – Sunny

+0

No deseaba mover los datos al directorio de Documentos porque no quiero que los datos (estáticos) sean copiados y contados en contra de la cuota de iCloud del usuario. Pero parece que en iOS 5.0.1 hay una manera de designar archivos para que no se realice una copia de seguridad: http://developer.apple.com/library/ios/#qa/qa1719/_index.html – Aneel

+2

Bueno, usted me inspiró y Después de pasar unas horas para resolver mi problema, escribí un artículo completo sobre esto [aquí] (http://blog.atwam.com/blog/2012/05/11/multiple-persistent-stores-and-seed-data -with-core-data /). Pensé que podría ayudar a otras personas en el futuro. – Wam

Respuesta

5

¿Ha intentado tener ambas configuraciones definidas en el mismo modelo (es decir, el mismo momd)? Puede hacerlo fácilmente seleccionando "Editor-> Agregar configuración" mientras edita uno de sus modelos de datos. Arrastre entidades para UserData y ItemData a la configuración adecuada. La configuración especificada de esta manera es lo que Core Data respeta; no se trata del nombre del archivo/URL. Una vez que haya hecho lo anterior, entonces simplifique su _managedObjectModel arriba para buscar el archivo/URL único de momd cada vez que se llame.

Alternativamente, si decide mantener dos archivos momd separados, asegúrese de que realmente haya definido sus modelos en las Configuraciones denominadas "UserData" y "ItemData" respectivamente en sus archivos de definición de modelo.

Mi sugerencia inicial es mantener un archivo de modelo. A menos que haya una razón por la cual estas configuraciones no puedan residir en el mismo modelo de objeto, no tiene sentido complicar las cosas con múltiples archivos. Creo que sería bastante difícil manipular a Core Data para hacer lo que se te pide que hagas arriba. Intenta simplificar la parte de modelado de tu código.

+1

Gracias por la respuesta. Tengo una buena razón para usar dos modelos separados. El modelo de elementos de datos se comparte con otro proyecto (la aplicación OS X utilizada para crear/editar el conjunto de datos). Si es posible, me gustaría poder mantener los dos modelos separados. – Aneel

+1

Intenté con lo que sugieres, y funciona. Copié el modelo de datos de usuario en el modelo de datos de elementos y creé dos configuraciones en XCode. Tengo que crear un PSC temporal y hacer una migración ligera sin configuración en cada una de las tiendas de datos, luego crear otra PSC y agregar cada tienda con la configuración adecuada. Sin esos pasos, sigo recibiendo errores. Con ellos, el PSC asocia cada entidad con la tienda correcta. Creo que el modelo unificado es menos elegante que mi otra solución de tener dos MOM/PSC/MOC por separado. ¡Gracias! Todavía me gustaría una forma de hacer esto con dos modelos separados. – Aneel

+1

Está bien, también probé lo que sugiere para mantener los modelos separados. ¡Eso también funciona! Parece que el núcleo de mi problema fue definir las configuraciones en programáticamente con ManagedObjectModel addEntities: forConfiguration: does not work. Aparecen cuando consultas al MOM para sus configuraciones, pero en realidad no parecen ser utilizados correctamente por el PSC. Crear las configuraciones en Xcode debe hacer más detrás de escena. – Aneel

Cuestiones relacionadas