我正在尝试执行需要MappingModel的iOS核心数据迁移。由于某种原因,核心数据无法使用映射模型,并且它会回退到自动轻量级迁移。
我启用了MigrationDebug选项以获取更多信息,我看到的内容毫无意义。映射模型的源哈希值和目标哈希值与源和目标ManagedObjectModels相同,忽略顺序。似乎应该使用映射模型,但日志显示“找不到合适的映射模型”。
这是(省略的)日志:
CoreData: annotation: (migration) will attempt automatic schema migration
CoreData: annotation: (migration) looking for mapping model with
source hashes:
{
TSBaseEntity = <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>;
TSBuyer = <91e837d1 3f348913 eff634d6 6fb9b3a6 747e2390 fbdc4ae6 32cc56d6 7582d4a8>;
...
}
destination hashes: {
TSBaseEntity = <4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>;
TSBuyer = <e316a857 8919c4be eef15387 5c67a21b 67d32919 99ead438 1ff93c05 2e065fcc>;
...
}
CoreData: annotation: (migration) checking mapping model at path file://localhost/Users/xandrews/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/0A84951E-21FC-47C0-A1B7-F880ACB672C4/Dev.app/Migrate_0_5_24To_0_5_27.cdm
source hashes:
{(
<4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>,
<91e837d1 3f348913 eff634d6 6fb9b3a6 747e2390 fbdc4ae6 32cc56d6 7582d4a8>,
...
)}
destination hashes: {(
<4797118c 50068f2f f544d9a9 4884720b 55ec7e4d 0d4c8f4e 1ee44be3 b06d2edc>,
<e316a857 8919c4be eef15387 5c67a21b 67d32919 99ead438 1ff93c05 2e065fcc>,
...
)}
CoreData: annotation: (migration) no suitable mapping model found
CoreData: annotation: (migration) inferring a mapping model between data models with
source hashes: ...
答案 0 :(得分:8)
Xcode 4生成的映射模型不会产生迁移发生所需的正确哈希值。您可以使用以下代码将迁移日志的输出与映射文件的哈希进行比较:
NSString *mappingModelPath = [[NSBundle mainBundle] pathForResource:@"MappingFile" ofType:@"cdm"];
NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:[NSURL fileURLWithPath:mappingModelPath]];
for (NSEntityMapping *entityMapping in mappingModel.entityMappings) {
NSLog(@"%@: %@", entityMapping.sourceEntityName, entityMapping.sourceEntityVersionHash);
NSLog(@"%@: %@", entityMapping.destinationEntityName, entityMapping.destinationEntityVersionHash);
}
您会看到这些与迁移日志输出中的哈希值不匹配。
解决方法是在Xcode 5中生成映射文件。
答案 1 :(得分:3)
在persistentStoreCoordinator方法中,给出这行代码
NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption, nil];
如果这没有帮助,那么您需要进行用户实施的迁移。因此,您必须使用源和目标模型创建映射模型。 在那种情况下,
NSDictionary *options=[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:NO],NSInferMappingModelAutomaticallyOption, nil];
使用以下代码
创建douce元数据if (sourceMetadata) {
NSString *configuration = nil;
NSManagedObjectModel *destinationModel = [self.persistentStoreCoordinator managedObjectModel];
//Our Source 1 is going to be incompatible with the Version 2 Model, our Source 2 won't be...
BOOL pscCompatible = [destinationModel isConfiguration:configuration compatibleWithStoreMetadata:sourceMetadata];
NSLog(@"Is the STORE data COMPATIBLE? %@", (pscCompatible==YES) ?@"YES" :@"NO");
if (pscCompatible == NO) {
migrationSuccess = [self performMigrationWithSourceMetadata:sourceMetadata toDestinationModel:destinationModel];
}
}
else {
NSLog(@"checkForMigration FAIL - No Source Metadata! \nERROR: %@", [error localizedDescription]);
}
并实现以下功能
- (BOOL)performMigrationWithSourceMetadata :(NSDictionary *)sourceMetadata toDestinationModel:(NSManagedObjectModel *)destinationModel
{
BOOL migrationSuccess = NO;
//Initialise a Migration Manager...
NSManagedObjectModel *sourceModel = [NSManagedObjectModel mergedModelFromBundles:nil
forStoreMetadata:sourceMetadata];
//Perform the migration...
if (sourceModel) {
NSMigrationManager *standardMigrationManager = [[NSMigrationManager alloc] initWithSourceModel:sourceModel
destinationModel:destinationModel];
//Retrieve the appropriate mapping model...
NSMappingModel *mappingModel = [NSMappingModel mappingModelFromBundles:nil
forSourceModel:sourceModel
destinationModel:destinationModel];
if (mappingModel) {
NSError *error = nil;
NSString *storeSourcePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes.sqlite"];
NSURL *storeSourceUrl = [NSURL fileURLWithPath: storeSourcePath];
NSString *storeDestPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Recipes2.sqlite"];
NSURL *storeDestUrl = [NSURL fileURLWithPath:storeDestPath];
//Pass nil here because we don't want to use any of these options:
//NSIgnorePersistentStoreVersioningOption, NSMigratePersistentStoresAutomaticallyOption, or NSInferMappingModelAutomaticallyOption
NSDictionary *sourceStoreOptions = nil;
NSDictionary *destinationStoreOptions = nil;
migrationSuccess = [standardMigrationManager migrateStoreFromURL:storeSourceUrl
type:NSSQLiteStoreType
options:sourceStoreOptions
withMappingModel:mappingModel
toDestinationURL:storeDestUrl
destinationType:NSSQLiteStoreType
destinationOptions:destinationStoreOptions
error:&error];
NSLog(@"MIGRATION SUCCESSFUL? %@", (migrationSuccess==YES)?@"YES":@"NO");
}
}
else {
//TODO: Error to user...
NSLog(@"checkForMigration FAIL - No Mapping Model found!");
abort();
}
return migrationSuccess;
}
答案 2 :(得分:1)
经过进一步的调查后,我发现我遇到了与此处提到的相同的问题(Core Data migration fails for to-one relationship)。
在我的关系中将最小值设置为1
而不是最小值,使Core Data使用我的自定义映射模型。虽然我不确定我是否认为这是Core Data中的一个错误
但是,这要求我更改原始(1.0)映射模型(用户已经在使用)。
我提出的修复是在1.0和2.0之间创建一个名为1.5的新映射模型。与1.0相比,1.5中唯一不同的是关系的最小值,即1.5设置为1
。
现在,我可以让Core Data执行从1.0到1.5的轻量级迁移,然后执行我自己的自定义迁移,从1.5到2.0。
虽然它可能是一个hacky解决方案,但它的工作完美。我粘贴了处理下面迁移的代码。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator != nil) {
return persistentStoreCoordinator;
}
NSURL *storeUrl = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Project.sqlite"]];
NSError *error;
NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeUrl error:&error];
if (! [[self managedObjectModel] isConfiguration:nil compatibleWithStoreMetadata:storeMetadata]) {
// The current store isn't compatible with the model and should be migrated, check the version identifier of the store
NSManagedObjectModel *sourceManagedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil forStoreMetadata:storeMetadata];
if ([[sourceManagedObjectModel versionIdentifiers] containsObject:@"Model_1_0"]) {
// The current version of the store is 1.0, a manual migration to 1.5 is needed in order to migrate finally to 2.0
NSURL *destinationModelURL = [[NSBundle mainBundle] URLForResource:@"Model.momd/Model_1_5" withExtension:@"mom"];
NSManagedObjectModel *destinationManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:destinationModelURL];
NSMigrationManager *migrationManager = [[NSMigrationManager alloc] initWithSourceModel:sourceManagedObjectModel destinationModel:destinationManagedObjectModel];
NSMappingModel *inferredMappingModel = [NSMappingModel inferredMappingModelForSourceModel:sourceManagedObjectModel destinationModel:destinationManagedObjectModel error:&error];
NSURL *destinationURL = [NSURL fileURLWithPath:[[self applicationDocumentsDirectory] stringByAppendingPathComponent:@"Migration.sqlite"]];
[migrationManager migrateStoreFromURL:storeUrl
type:NSSQLiteStoreType
options:nil
withMappingModel:inferredMappingModel
toDestinationURL:destinationURL
destinationType:NSSQLiteStoreType
destinationOptions:nil
error:&error];
if (error) {
DLog(@"Failed to migrate store from URL %@ with mapping model %@ to destination URL %@ with error %@", storeUrl, inferredMappingModel, destinationURL, error);
}
[[NSFileManager defaultManager] removeItemAtURL:storeUrl error:&error];
[[NSFileManager defaultManager] moveItemAtURL:destinationURL toURL:storeUrl error:&error];
}
}
NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption: [NSNumber numberWithBool:YES],
NSInferMappingModelAutomaticallyOption: [NSNumber numberWithBool:NO] };
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
/*Error for store creation should be handled in here*/
DLog(@"%@", error);
}
return persistentStoreCoordinator;
}
答案 3 :(得分:0)
映射模型可能不足以处理迁移。在这种情况下,即使映射模型与源模型和目标模型匹配,也不会加载映射模型。
为迁移编写测试。逐步重做对模型的更改并测试迁移。这样您就会发现阻止映射模型加载的更改。
了解: 重命名属性或创建属性而不指定默认值。 将属性更改为非可选。
在这些情况下,您必须在映射模型中手动指定行为。
答案 4 :(得分:0)
有同样的问题。我删除了一个实体并相应地重命名了关系字段。我首先尝试使用轻量级迁移,因此为关系指定了重命名ID。由于疏忽,我混淆了用于“重命名ID”和“哈希修改器”的字段。一旦纠正,一切都按预期工作。