此问题询问以下场景中的最佳实践:
随附的图像显示了我的工作订单和服务核心数据实体。请注意,删除规则当前为工单无操作。 (请注意,更改为 Nullify 不会解决我的问题,只会导致相同的问题)。另请注意,在服务上我对 id 有限制。这不允许重复。因此,我添加了以下合并策略:
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
合并策略将采用我发送的新数据并覆盖数据库中的默认数据。如果没有这个,我的程序将抛出一个错误及其编写方式。
如果我使用这些设置运行我的代码,并且我对工作订单而不是服务进行批量删除(因为我想保留这些),那么当我重新启动程序时,当我尝试添加 * 时,它会崩溃*对具有相同 ID 的服务的引用。
我的问题是为什么它会崩溃以及解决此问题的最佳方法是什么?我当前的理论是这些实体可能有另一个唯一标识符,并且因为我删除了工作订单,所以它的引用是到服务的不同上下文版本...当我使用与旧服务相同的 id 创建新服务时,它可能假定相同的内部 id。我不确定这是否正在发生或如何确认。
我的代码发生在我的一个 Controller 的 viewDidLoad 方法中,如下所示。
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
let context = gm_getContext()
//Create default fetch request to get all workorders
let fetchRequest: NSFetchRequest<Workorders> = Workorders.fetchRequest()
do{
//Run fetch request to get search results.
let searchResults = try context.fetch(fetchRequest)
//If no results were found and demo mode = true, lets create some default records.
if(searchResults.count<=0 && g_demoMode==true){
print("create default data")
//Uncomment the following lines if you want to prove that the Merge Policy
//Is working for Unique Constraints.
let serviceFetchRequest: NSFetchRequest<Service> = Service.fetchRequest()
let serviceSearchResults = try context.fetch(serviceFetchRequest)
print("Services Count = \(serviceSearchResults.count)")
//First we have to create a sample service
let entity = NSEntityDescription.entity(forEntityName: "Service", in: context)
let service = NSManagedObject(entity: entity!, insertInto: context)
service.setValue(1, forKey: "id")
service.setValue("Tire Repair Service Sample", forKey: "name")
service.setValue("<html>Test Service Field</html>",forKey:"templatedata")
//add reference to the global
g_services.append(service as! Service)
//Proof that service is indeed a Service object and stored in global
print("g_services[0].name = "+g_services[0].name!)
//Save the service object (overwriting an old one with same id if needed)
do {
try context.save()
print("Saved context with service")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
print("Could not save, unknown error")
}
//Now create 3 sample work orders all using the same service template.
let workorderEntity1 = NSEntityDescription.entity(forEntityName: "Workorders", in: context)
let workorder1 = NSManagedObject(entity: workorderEntity1!, insertInto: context)
print("created work order variable 1")
workorder1.setValue(1, forKey: "id")
workorder1.setValue("11402 Kensington Rd, Los Alamitos, CA, 90720", forKey: "address")
workorder1.setValue("33.797472", forKey: "lat")
workorder1.setValue("-118.084136", forKey: "lng")
workorder1.setValue(15,forKey: "client_id")
workorder1.setValue("Need to fix their tire fast", forKey: "desc")
workorder1.setValue("(562)810-4384", forKey: "phone")
workorder1.setValue(g_services[0], forKey: "service")
print("Created first work order")
let workorderEntity2 = NSEntityDescription.entity(forEntityName: "Workorders", in: context)
let workorder2 = NSManagedObject(entity: workorderEntity2!, insertInto: context)
workorder2.setValue(2, forKey: "id")
workorder2.setValue("17078 Greenleaf Street, Fountain Valley, CA, 92708", forKey: "address")
workorder2.setValue("33.714992", forKey: "lat")
workorder2.setValue("-117.958874", forKey: "lng")
workorder2.setValue(16,forKey: "client_id")
workorder2.setValue("This guy does not know what he wants", forKey: "desc")
workorder2.setValue("(562)777-3344", forKey: "phone")
workorder2.setValue(g_services[0], forKey: "service")
let workorderEntity3 = NSEntityDescription.entity(forEntityName: "Workorders", in: context)
let workorder3 = NSManagedObject(entity: workorderEntity3!, insertInto: context)
workorder3.setValue(3, forKey: "id")
workorder3.setValue("17045 South Pacific Avenue", forKey: "address")
workorder3.setValue("33.713565", forKey: "lat")
workorder3.setValue("-118.067535", forKey: "lng")
workorder3.setValue(17,forKey: "client_id")
workorder3.setValue("Tire damaged by the beach", forKey: "desc")
workorder3.setValue("(714)234-5678", forKey: "phone")
workorder3.setValue(g_services[0], forKey: "service")
//Don't need signature, pictures and videos because they just don't exist yet.
//add reference to the global
g_workOrders.append(workorder1 as! Workorders)
g_workOrders.append(workorder2 as! Workorders)
g_workOrders.append(workorder3 as! Workorders)
print("Preparing to save to context for work orders")
//Save the work order objects (overwriting any old ones with same id if needed)
do {
try context.save()
print("Saved context with workorders")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
print("Could not save, unknown error")
}
}else{
print("WorkOrders Count = \(searchResults.count)")
let workorderFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Workorders")
//let workorderFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Workorders")
let deleteWorkOrderRequest = NSBatchDeleteRequest(fetchRequest: workorderFetchRequest) //Deletes ALL workorders
//Perform Actual Deletion On Database Tables
do{
try context.persistentStoreCoordinator!.execute(deleteWorkOrderRequest, with: context)
}catch{
fatalError("Bad Things Happened \(error)")
}
print("deleted workorders")
}
} catch {
print("Error with request: \(error)")
}
print("service table view controller loaded")
}
用于跟踪 coreData 值的上下文和全局变量是在这样的 globals.swift 文件中全局定义的。
var g_workOrders = [Workorders]()
var g_services = [Service]()
//Shortcut method to get the viewcontext easily from anywhere.
func gm_getContext () -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
//For unique constraints it will overwrite the data.
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return context
}
其他注释和我尝试过的事情:
我知道它在这一行崩溃 (workorder1.setValue(g_services[0], forKey: "service"))
,这就是我知道它与服务相关的方式,并将规则更改为工作订单的级联删除修复了崩溃,但它删除了附加到它的服务! ...这是有道理的,但不是我想要的。
最佳答案
我最近找到了我的问题的答案,并且该问题与多个因素有关。
首先,我的核心数据堆栈设置不正确。我现在已将其更改为此(由我友好的开发人员 friend 指出)。
import UIKit
import CoreData
class DataController: NSObject {
var managedObjectContext: NSManagedObjectContext
static var dataController: DataController!
override init() {
// This resource is the same name as your xcdatamodeld contained in your project.
guard let modelURL = Bundle.main.url(forResource: "WorkOrders", withExtension: "momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOf: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = psc
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docURL = urls[urls.endIndex-1]
/* The directory the application uses to store the Core Data store file.
This code uses a file named "DataModel.sqlite" in the application's documents directory.
*/
let storeURL = docURL.appendingPathComponent("WorkOrders.sqlite")
do {
let options = [NSSQLitePragmasOption: ["journal_mode": "DELETE"]]
try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)
} catch {
fatalError("Error migrating store: \(error)")
}
}
class func sharedInstance() -> DataController {
if (dataController != nil) {
return dataController
}
dataController = DataController()
return dataController
}
}
每当我需要访问 coreData 时,我现在就应该这样做......
let context = DataController.sharedInstance().managedObjectContext
另一件事需要注意的是数据 Controller 中的并发设置被设置为在主线程上工作。这也是问题的一部分,因为我在线程中运行代码。
它在 DataController 中的这一行设置为主线程
managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
因此,每次您要访问或将数据保存到 coreData 时,总是将其包装在对主线程的调用中,如下所示......
DispatchQueue.main.async {
AppDelegate.appDelegate.saveContext()
}
最后,我遇到的最后一个问题是我使用下面的命令进行批量删除。
let workorderFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Workorders")
let deleteWorkOrderRequest = NSBatchDeleteRequest(fetchRequest: workorderFetchRequest) //Deletes ALL workorders
let context = DataController.sharedInstance().managedObjectContext
//Save the work order objects (overwriting any old ones with same id if needed)
do {
try context.execute(deleteWorkOrderRequest)
context.reset()
print(">>> cleared old data!")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
print("Could not save, unknown error")
}
这里的关键是了解批处理命令当前直接在数据库上工作并忽略托管上下文,这意味着我运行此命令后我的托管上下文和数据库变得不同步。简单的解决方法是始终确保执行批处理命令后运行...
context.reset()
这会将数据从数据库强制加载回托管上下文,以便一切都同步。在我进行这些更改后,一切正常。希望这对某人有帮助。
关于swift - 删除相互依赖的对象时了解核心数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40415856/