迁移 Core Data 版本时“不能两次添加同一个商店”

Posted

技术标签:

【中文标题】迁移 Core Data 版本时“不能两次添加同一个商店”【英文标题】:"Can't add the same store twice" on migration of Core Data versions 【发布时间】:2015-02-23 18:09:58 【问题描述】:

我正在尝试在两个 CoreData 版本之间执行迁移,这与新版本有两种配置这一事实根本不同,因此我可以将实体保存在两个不同的 sqlite 文件中(一个在 Main Bundle 中是只读的,一个是旧的 sqlite,现在仅用于用户数据)。我还添加了一个模型映射来映射差异,尤其是重置由于多个配置而丢失的实体之间的关系。 如果从头开始安装并且新记录按预期插入到应用程序文档目录中的用户数据 sqlite 中,则该应用程序可以正常工作而不会崩溃。 问题是当我尝试从以前的 Core Data 版本迁移时:应用程序启动时没有崩溃,但在第一次 executeFetchRequest 它在 coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: "Userdata", URL: urlUser, options: optionsUser, error: &errorUser) 崩溃(通过调试查看)并且控制台返回此日志:

2015-02-23 18:48:24.052 MedAbility[21480:585606] CoreData:错误: -addPersistentStoreWithType:SQLite 配置:(null) URL:file:///Volumes/Documenti/Users/andrea/Library/Developer/CoreSimulator/Devices/301801BE-4B6A-4371-BE66-3E71557B0897/data/Containers/Data/Application/ FBEC0124-1D74-4F94-99F2-6F492281AA8E/Documents/MedAbility.sqlite 选项: NSInferMappingModelAutomaticallyOption = 1; NSMigratePersistentStoresAutomaticallyOption = 1; ...返回错误错误域=NSCocoaErrorDomain代码=134080“操作 无法完成。 (可可错误 134080。)“用户信息 = 0x7fb50ae481d0 NSUnderlyingException=不能两次添加同一个商店与 userInfo 字典 NSUnderlyingException = "不能两次添加同一个商店"; 2015-02-23 18:48:24.061 MedAbility [21480:585606] CoreData:错误: -addPersistentStoreWithType:SQLite 配置:用户数据 URL:file:///Volumes/Documenti/Users/andrea/Library/Developer/CoreSimulator/Devices/301801BE-4B6A-4371-BE66-3E71557B0897/data/Containers/Data/Application/FBEC0124- 1D74-4F94-99F2-6F492281AA8E/Documents/MedAbility.sqlite 选项: NSInferMappingModelAutomaticallyOption = 1; NSMigratePersistentStoresAutomaticallyOption = 1; ...返回错误错误域=NSCocoaErrorDomain代码=134080“操作 无法完成。 (可可错误 134080。)“用户信息 = 0x7fb50ae481d0 NSUnderlyingException=不能两次添加同一个商店与 userInfo 字典 NSUnderlyingException = "不能两次添加同一个商店";

这是我的代码:

旧版 PersistentStoreCoordinator:

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = 
        // Create the coordinator and store
        var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("MedAbility.sqlite")
        var error: NSError? = nil
        var failureReason = "There was an error creating or loading the application's saved data."
        // Set options for migrations
        let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
        if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: options, error: &error) == nil 
            coordinator = nil
            // Report any error we got.
            let dict = NSMutableDictionary()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
            dict[NSLocalizedFailureReasonErrorKey] = failureReason
            dict[NSUnderlyingErrorKey] = error
            error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)

            NSLog("Unresolved error \(error), \(error!.userInfo)")
        

        // Exclude db from backup
        Util.addSkipBackupAttributeToItemAtURL(url)

        return coordinator
        ()

新版本 PersistentStoreCoordinator:

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = 
        // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
        // Create the coordinator and store
        var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

        let url = NSBundle.mainBundle().URLForResource("MedAbilityDomande", withExtension: "sqlite")
        var error: NSError? = nil
        var failureReason = "There was an error creating or loading the application's saved data."
        // Set questions sqlite as read-only
        let options = [NSReadOnlyPersistentStoreOption: true]
        if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: "Domande", URL: url, options: options, error: &error) == nil 
            coordinator = nil
            // Report any error we got.
            let dict = NSMutableDictionary()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
            dict[NSLocalizedFailureReasonErrorKey] = failureReason
            dict[NSUnderlyingErrorKey] = error
            error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)

            NSLog("Unresolved error \(error), \(error!.userInfo)")
        

        let urlUser = self.applicationDocumentsDirectory.URLByAppendingPathComponent("MedAbility.sqlite")
        var errorUser: NSError? = nil
        var failureReasonUser = "There was an error creating or loading the application's saved data."
        // Set migration options
        let optionsUser = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
        if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: "Userdata", URL: urlUser, options: optionsUser, error: &errorUser) == nil 
            coordinator = nil
            // Report any error we got.
            let dict = NSMutableDictionary()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
            dict[NSLocalizedFailureReasonErrorKey] = failureReasonUser
            dict[NSUnderlyingErrorKey] = errorUser
            errorUser = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)

            NSLog("Unresolved error \(errorUser), \(errorUser!.userInfo)")
        

        return coordinator
        ()

有人知道我该如何解决这个问题吗?

感谢您的帮助!

编辑

我做了一些测试,发现为第一个数据库评论coordinator!.addPersistentStoreWithType (NSSQLiteStoreType, configuration: "Questions", URL: url, options: options, error: & error),应用程序启动没有问题,显然没有添加数据库的内容。如果我反转两个数据库的添加,则应用程序崩溃并使用相同的日志,除了“URL:文件:[...]”,它在前第一个现在第二个数据库(“MedAbilityDomande.sqlite”)上调用;实际上,问题在于添加第二个数据库而不是特定数据库。似乎将旧数据库迁移到用于用户数据的新数据库实际上并不会迁移配置,因此旧配置仍然会与插入到 mainBundle 中的新只读数据库重叠实体。正如我之前所说,如果应用程序从头开始(无需迁移)可以完美运行。

你有什么想法吗?

再次感谢您!

【问题讨论】:

【参考方案1】:

我终于找到了解决办法!我基于要迁移以用于用户数据的数据库创建了数据库MedAbilityDomande.sqlite。这导致两个具有相同 UUID 的数据库生成错误 "Can't add the same store twice"。在应用程序包 (MedAbilityDomande.sqlite) 中更改数据库的 UUID 就足够了,一切正常。

下次见!

【讨论】:

您好,我也遇到了这个问题,您是如何更改捆绑中使用的数据库的 UUID 的? 嗨!我使用软件打开数据库(我使用“DB Browser for SQLite”)。然后我手动更改了表“Z_METADATA”的“Z_UUID”列上的 UUID 字符串。为了确保使用有效且唯一的 UUID,我使用临时应用程序创建了一个新的 CoreData 数据库,然后始终使用“DB Browser for SQLite”,我将 UUID 从新数据库复制并粘贴到我想要的数据库中包含在捆绑包中。 非常感谢,我可能需要尝试一下

以上是关于迁移 Core Data 版本时“不能两次添加同一个商店”的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 中使用数据模型迁移时应用程序崩溃

Core Data 轻量级迁移后执行自定义代码

当涉及多个设备时,Core Data iCloud 迁移“丢失”数据

考虑使用 Core Data 轻量级迁移的多个数据模型版本

在哪里可以找到有关 Core Data 对象模型版本控制和迁移的更多详细信息?

轻量级迁移后如何从 Core Data 中删除数据