Core Data 错误地选择了映射模型

Posted

技术标签:

【中文标题】Core Data 错误地选择了映射模型【英文标题】:Core Data incorrectly selects Mapping Model 【发布时间】:2014-07-05 02:44:20 【问题描述】:

我正在开发一个使用 Core Data 来管理其持久数据的应用程序。我们一直在进行足够复杂的架构更改,以至于轻量级自动迁移不兼容,因此我们改用手动定义的模型迁移。

我的问题是,当我调用 addPersistentStoreWithType:configuration:URL:options:error: 并设置了 NSMigratePersistentStoresAutomaticallyOption 时,它选择了错误的映射模型。如果我手动调用 mappingModelFromBundles:forSourceModel:destinationModel:,我会返回相同的(不正确的)映射模型。

我们有三个版本的数据模型:1.0、1.1 和 1.1.1。 我们定义了三种映射模型:1.0-1.1、1.0-1.1.1 和 1.1-1.1.1。 应用中数据的版本是1.1 数据的当前版本是1.1.1 选择的映射模型是 1.0-1.1.1。 这会导致在 1.1 中添加的表被删除并重新创建,从而丢失数据。

当我使用 -com.apple.CoreData.MigrationDebug 1 命令行选项时,它会打印出升级过程的调试信息。它显示了当前数据集中的所有表(包括 1.1 中添加的表)及其哈希值。然后显示 1.0-1.1.1 映射模型的源和目标哈希值,声明它是一个兼容的映射模型,并且从不检查任何其他模型。

如果我使用 [NSMappingModel initWithContentsOfURL] 专门选择 1.1-1.1.1 映射模型,我会得到所需的行为。

我有点不知所措,因为在我看来,它不应该愿意使用源哈希与当前数据集的哈希不匹配的映射模型。

这里是一些选定的输出,显示正在重置的表在数据库中,但不在映射模型中。我已经验证了 1.1 数据散列和 1.0 数据散列之间的唯一区别是 1.1 有一个添加的表。

2014-07-03 23:03:20.547 XXX[50242:60b] CoreData: annotation: (migration)     will attempt automatic schema migration
2014-07-03 23:03:21.212 XXX[50242:60b] CoreData: annotation: (migration) looking for mapping model with source hashes: 

    XXX = <77891dab a2acaff3 e52f71e3 0f8c3d5f eee99f43 f549e3fc 72eddd29 b2af83fd>;
    XXX = <6049b4fb 7d8c4e5f 63149e35 5abfd274 1264c2f9 76d13cf3 cc69a23a e29edac8>;
    XXX = <cf3bd61a 71c2838c 421c6e50 41abd013 b0c153cb b165a0e6 21d5c352 f29b5743>;
    XXX = <fd8c6be5 b97b3827 455a620c 3e6ff6e9 e2e09afd 472b9cbf 07d11e29 d5a52159>;
    XXX = <6cf5aac1 67ead46a fbaf8450 11c2c0b9 dcc1e2ae dd3bbf86 06d09b78 4d4b6bbe>;
    XXX = <09942be7 56f82126 d48a90b2 e6cf08e7 1fe9c091 1ee7fec8 8d426ca4 a00af268>;
    XXX = <40462ca4 098ae4d2 d3e8e7cf a55bc7df ca58c8c9 3aaf8d94 b681080c 63e5683b>;
    XXX = <dce53740 e8aba89f ac8180b4 0f297821 d09734a1 8ea3c344 8cb9dd6c d3baf645>;
    XXX = <c9f7a2e3 13518dac 5ae5209d 239d1c11 0fd3f11f 5366b7d4 fd3a97d3 3e3d41d1>;
    XXX = <e5bf6c2f c0c9d818 e1b4e2cc 9b7a92e3 2cd6bed4 5a98e6c3 53619376 9b3951ef>;
    XXX = <5431c046 d30e7f32 c2cc8099 58add1e7 579ad104 a3aa8fc4 846e97d7 af01cc79>;
    XXX = <3aef2c65 c9647274 1302fe8a fdca7ce6 cb87c7df 2751a19f bd946707 c8244729>;
    THIS_ONE = <1bb3a3b9 857bcdf2 ca573238 a86672b8 0486929e ed0357c8 72879022 e12efe37>;
    XXX = <9f627411 6f8c0891 3693eab1 aa45cbd3 0143b28e 1e3584da e6ea2867 554a26ad>;
    XXX = <9def8e9f 14dfb358 b5694bce 77759b7c c1901fe1 3e3a163b 80061b51 268089a8>;
    XXX = <06d0b355 4fb4ff4b 0adf05d4 8ce0378c 4aa156e9 a09c8a16 a82d8376 1fd2f929>;
    XXX = <fb9db76f 350ad944 88e1cf5d 15aaca9c 230355f9 13a2dace 62d5e4fb 0a2ecd7a>;
    XXX = <715d9149 7aea98db fdb3a2fa e1682e12 dfe8f63a d09aac57 301be349 91fffd44>;
    XXX = <002c8d92 8bc08e6c eb34fe0c a10ef78e ed050a8e 17a86e63 9911adb8 e2c36df1>;
    XXX = <362ea015 28a5c834 47b125c1 c460dd62 f0172785 e024b8aa 17dc544f 66871077>;
    XXX = <bd06507d f33ee72d d6bba2d5 c29eb8c5 1f87568b 186ab250 7312c0ec 6f2cd09c>;
    XXX = <22ff0e46 f56dbc7a e8e92cf6 9090a451 742517ff 7d29838d 0cd41e9e a3615134>;
    XXX = <ec7834a0 987c4c5f df40ade2 73075b11 e329a018 94fe47ca 08f7c9ed 95bb4da1>;
    XXX = <c3feffd4 8d223692 d314720a 4496b787 871db7d0 31097cff e1225b9b 6275e613>;
    XXX = <e5e3c8aa 5267d778 9fd62dc5 884ef416 5f836890 d82fed79 efd3796d bcb58503>;
    XXX = <6705e1bf cac0c2ed b040b64f ca1f6e6e 74332890 907ec136 7a99606c e116a946>;
    XXX = <856a10a5 18de663a 1860ea0d c0bd9295 769e4a42 99420fb5 02314b22 f39fe1a4>;
    XXX = <35f6c30f a146166c 6e132297 bf463c59 756b8071 49aae2d7 ec6b6de8 fb7f7300>;
    XXX = <ec2a9e60 6ae28042 a62429e4 b0ec5939 3734e0ac 9a919421 a9fbede2 031b0bf6>;
    XXX = <b08fbebb 9100df77 5aba3640 c8237a5b 4ddbed50 fb6cb28c 439c7e37 9b2ccb4a>;
    XXX = <95c8cfb8 a1aafabc 90a9231b 0ef15d85 10e30393 5cfd4921 4db4a12f 511c8977>;
    XXX = <4e0fcdb8 4fbf9aa3 684875aa c54a4c5d c02020b2 d29212e4 587069d2 eed3aa31>;
    XXX = <ad580044 b972d6ab df963bda ad071ba5 9c82aab5 4007f377 bf8858fe b9bc6274>;
    XXX = <4fc9af50 0722da5d 18e0b755 63cf2a04 88e8b2d3 e8196ec2 375171b1 ce40fb4e>;


2014-07-03 23:03:21.231 XXX[50242:60b] CoreData: annotation: (migration) checking mapping model /Users/jonathan/Library/Application Support/iPhone Simulator/7.1/Applications/XXX/XXX.app/1.0-1.1.1.cdm
 source hashes: 
(
    <09942be7 56f82126 d48a90b2 e6cf08e7 1fe9c091 1ee7fec8 8d426ca4 a00af268>,
    <40462ca4 098ae4d2 d3e8e7cf a55bc7df ca58c8c9 3aaf8d94 b681080c 63e5683b>,
    <ec7834a0 987c4c5f df40ade2 73075b11 e329a018 94fe47ca 08f7c9ed 95bb4da1>,
    <b08fbebb 9100df77 5aba3640 c8237a5b 4ddbed50 fb6cb28c 439c7e37 9b2ccb4a>,
    <77891dab a2acaff3 e52f71e3 0f8c3d5f eee99f43 f549e3fc 72eddd29 b2af83fd>,
    <e5e3c8aa 5267d778 9fd62dc5 884ef416 5f836890 d82fed79 efd3796d bcb58503>,
    <4e0fcdb8 4fbf9aa3 684875aa c54a4c5d c02020b2 d29212e4 587069d2 eed3aa31>,
    <bd06507d f33ee72d d6bba2d5 c29eb8c5 1f87568b 186ab250 7312c0ec 6f2cd09c>,
    <5431c046 d30e7f32 c2cc8099 58add1e7 579ad104 a3aa8fc4 846e97d7 af01cc79>,
    <ad580044 b972d6ab df963bda ad071ba5 9c82aab5 4007f377 bf8858fe b9bc6274>,
    <c9f7a2e3 13518dac 5ae5209d 239d1c11 0fd3f11f 5366b7d4 fd3a97d3 3e3d41d1>,
    <3aef2c65 c9647274 1302fe8a fdca7ce6 cb87c7df 2751a19f bd946707 c8244729>,
    <6705e1bf cac0c2ed b040b64f ca1f6e6e 74332890 907ec136 7a99606c e116a946>,
    <856a10a5 18de663a 1860ea0d c0bd9295 769e4a42 99420fb5 02314b22 f39fe1a4>,
    <6049b4fb 7d8c4e5f 63149e35 5abfd274 1264c2f9 76d13cf3 cc69a23a e29edac8>,
    <6cf5aac1 67ead46a fbaf8450 11c2c0b9 dcc1e2ae dd3bbf86 06d09b78 4d4b6bbe>,
    <715d9149 7aea98db fdb3a2fa e1682e12 dfe8f63a d09aac57 301be349 91fffd44>,
    <e5bf6c2f c0c9d818 e1b4e2cc 9b7a92e3 2cd6bed4 5a98e6c3 53619376 9b3951ef>,
    <fb9db76f 350ad944 88e1cf5d 15aaca9c 230355f9 13a2dace 62d5e4fb 0a2ecd7a>,
    <35f6c30f a146166c 6e132297 bf463c59 756b8071 49aae2d7 ec6b6de8 fb7f7300>,
    <9def8e9f 14dfb358 b5694bce 77759b7c c1901fe1 3e3a163b 80061b51 268089a8>,
    <9f627411 6f8c0891 3693eab1 aa45cbd3 0143b28e 1e3584da e6ea2867 554a26ad>,
    <dce53740 e8aba89f ac8180b4 0f297821 d09734a1 8ea3c344 8cb9dd6c d3baf645>,
    <95c8cfb8 a1aafabc 90a9231b 0ef15d85 10e30393 5cfd4921 4db4a12f 511c8977>,
    <cf3bd61a 71c2838c 421c6e50 41abd013 b0c153cb b165a0e6 21d5c352 f29b5743>,
    <ec2a9e60 6ae28042 a62429e4 b0ec5939 3734e0ac 9a919421 a9fbede2 031b0bf6>,
    <c3feffd4 8d223692 d314720a 4496b787 871db7d0 31097cff e1225b9b 6275e613>,
    <06d0b355 4fb4ff4b 0adf05d4 8ce0378c 4aa156e9 a09c8a16 a82d8376 1fd2f929>,
    <4fc9af50 0722da5d 18e0b755 63cf2a04 88e8b2d3 e8196ec2 375171b1 ce40fb4e>,
    <002c8d92 8bc08e6c eb34fe0c a10ef78e ed050a8e 17a86e63 9911adb8 e2c36df1>,
    <fd8c6be5 b97b3827 455a620c 3e6ff6e9 e2e09afd 472b9cbf 07d11e29 d5a52159>,
    <362ea015 28a5c834 47b125c1 c460dd62 f0172785 e024b8aa 17dc544f 66871077>,
    <22ff0e46 f56dbc7a e8e92cf6 9090a451 742517ff 7d29838d 0cd41e9e a3615134>
)

2014-07-03 23:03:21.233 XXX[50242:60b] CoreData: annotation: (migration) found compatible mapping model /Users/jonathan/Library/Application Support/iPhone Simulator/7.1/Applications/XXX/XXX.app/1.0-1.1.1.cdm

我使用的是 ios 7.1 和 Xcode 5.1.1。

如前所述,1.0 和 1.1 之间的唯一变化似乎是添加了一个表格。我猜他们使用的哈希比较函数不认为这是冲突?我的下一个尝试是在使用字母搜索的假设下重命名迁移,以便首先找到并检查 1.1-1.1.1 迁移。除此之外,我预计我将不得不添加某种手动逻辑(通过子类化某些东西?)以强制将附加表视为架构不匹配。

【问题讨论】:

我遇到了同样的情况,无法让自动选择起作用。最终我写了一个手动映射模型选择方法。这比调试自动选择所花费的时间要少。推荐一件事:验证映射模型是否与它们各自的数据模型完全匹配。 谢谢奥拉夫。您对如何实现手动选择方法有任何参考吗? 抱歉,我没有单一的参考资料可以说明如何操作。我收集了一些零碎的东西,然后将它们粘贴在一起。我会用一些示例代码发布答案 【参考方案1】:

这不是自动选择映射模型失败的原因。正如我在评论中所写,我确实遇到了类似的问题并编写了自己的(手动)映射模型选择。

对于手动选择,我们需要知道用于创建当前商店的模型。 所有实体(表)都有一个版本哈希,所以这个哈希可以用来识别模型版本。

在应用启动后,但在加载持久存储之前,映射模型选择会循环遍历可能的数据模型,即 1.0.xcdatamodel、1.1.xcdatamodel 和 1.1.1.xcdatamodel

该逻辑将实体THIS_ONE已知 哈希与持久存储中找到 的版本哈希进行比较。 已知 版本哈希THIS_ONE 来自数据模型文件。如果数据模型用于创建存储,则哈希匹配。

因此,应用程序遍历已知模型名称列表(即“1.0”、“1.1”和“1.1.1”)并调用匹配方法isModel:forStore:。如果返回是YES,那么我们找到了匹配的模型。

现在我们知道了源数据模型,我们可以识别映射模型。下一步是使用适当的映射文件启动实际迁移 migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:

这是匹配版本哈希的方法:

-(BOOL)isModel:(NSString *)modelUsed forStore:(NSURL *)storeUrl 
        NSString *modelFound = @"unknown Model";
        NSDictionary *knownTHIS_ONEHashes = [self knownTHIS_ONEHashes];
        NSFileManager *fileManager = [NSFileManager defaultManager];

        BOOL doesExistCurrentStore = [fileManager fileExistsAtPath:[storeUrl path]];
        if (doesExistCurrentStore) 
            NSError *error = nil;
            NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeUrl error:&error];

            if (nil == storeMetadata)   // no source meta data => dont know if need to migrate
                NSLog(@"sourceMetadata is nil");
             else 
                NSLog(@"sourceMetadata is %@", storeMetadata);
                NSDictionary *storeHashes = [storeMetadata objectForKey:@"NSStoreModelVersionHashes"];
                NSData *curentTHIS_ONEHash  = storeHashes[@"THIS_ONE"];
                for (NSString *modelName in [knownTHIS_ONEHashes allKeys]) 
                    if ([knownTHIS_ONEHashes[modelName] isEqualToData:curentTHIS_ONEHash]) 
                        NSLog(@"found matching model: %@",modelName);
                        modelFound = modelName;
                        break;
                    
                
            
         // else store does not exist so there is no need for a data migration
        return   ([modelUsed isEqualToString:modelFound]);
    

在运行时从包中存在的模型中读取已知的哈希值。 _kModelNameVx 是硬编码的型号名称。

 -(NSDictionary *)knownTHIS_ONEHashes 
        NSMutableDictionary *returnDict = [NSMutableDictionary new];
        NSArray *knowModelFiles = @[_kModelNameV1, _kModelNameV2, _kModelNameV3, _kModelNameV4];
        NSString * destinationModelPath;
        NSURL * destinationModelURL;
        NSManagedObjectModel * destinationModel;
        for (NSString *singleFile in knowModelFiles) 
            destinationModelPath = [[NSBundle mainBundle] pathForResource:singleFile
                                                                   ofType:@"mom"
                                                              inDirectory:@"<xcdatamodel_name>.momd"];
            destinationModelURL = [NSURL fileURLWithPath:destinationModelPath];
            destinationModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:destinationModelURL];
            NSDictionary *modelMetadata = [destinationModel entityVersionHashesByName];
            NSData *THIS_ONEHash = [modelMetadata objectForKey:@"THIS_ONE"];
            [returnDict setValue:THIS_ONEHash forKey:singleFile];
        
        return returnDict;
    

【讨论】:

以上是关于Core Data 错误地选择了映射模型的主要内容,如果未能解决你的问题,请参考以下文章

有两个相同模型版本时的 Core Data 迁移

Core Data 的推断映射模型创建(轻量级迁移)崩溃。线程问题?

如何为一对多关系正确配置 Core Data 数据模型和 NSManagedObject?

如何有条件地将多个模型映射到 ASP.NET Core Identity 中的一个表(AspNetUsers)?

Core Data基础

Core Data-备用