Core Data unique attributes
是否可以使 Core Data 属性唯一,即没有两个 MyEntity 对象可以具有相同的 myAttribute?
我知道如何以编程方式执行此操作,但我希望有一种方法可以使用 xcode 中的图形数据模型编辑器来执行此操作。
我正在使用 iPhone 3.1.2 SDK。
每次在对象上创建时,我都会执行一个类方法,该方法仅在另一个实体不存在时创建一个新实体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | + (TZUser *)userWithUniqueUserId:(NSString *)uniqueUserId inManagedObjectContext:(NSManagedObjectContext *)context { TZUser *user = nil; NSFetchRequest *request = [[NSFetchRequest alloc] init]; request.entity = [NSEntityDescription entityForName:@"TZUser" inManagedObjectContext:context]; request.predicate = [NSPredicate predicateWithFormat:@"objectId = %@", uniqueUserId]; NSError *executeFetchError = nil; user = [[context executeFetchRequest:request error:&executeFetchError] lastObject]; if (executeFetchError) { NSLog(@"[%@, %@] error looking up user with id: %i with error: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [uniqueUserId intValue], [executeFetchError localizedDescription]); } else if (!user) { user = [NSEntityDescription insertNewObjectForEntityForName:@"TZUser" inManagedObjectContext:context]; } return user; } |
从 IOS 9 开始,有一种处理唯一约束的新方法。
您在数据模型中定义独特的属性。
您需要设置托管上下文合并策略"合并策略单例对象,该策略定义了在保存操作期间处理冲突的标准方法"NSErrorMergePolicy 是默认设置,如果存在任何合并冲突,此策略会导致保存失败。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
[_managedObjectContext setMergePolicy:NSOverwriteMergePolicy];
return _managedObjectContext;
}
Apple Ducumentation Merge Policy 中讨论了各种选项
这里得到了很好的回答
扎卡里·奥尔的回答
他还创建了一篇博文和示例代码。
示例代码
博文
最具挑战性的部分是让数据模型属性可编辑。秘诀是左键单击,然后右键单击,然后单击符号添加约束。
我决定使用
例如:
1 2 3 | - (BOOL)validateMyAttribute:(id *)value error:(NSError **)error { // Return NO if there is already an object with a myAtribute of value } |
感谢 Martin Cote 的意见。
您可以覆盖
1 2 3 4 5 6 | - (void)setMyAttribute:(id)value { NSArray *objects = [self fetchObjectsWithMyValueEqualTo:value]; if( [objects count] > 0 ) // ... throw some exception [self setValue:value forKey:@"myAttribute"]; } |
如果你想确保每个
您只需要检查现有的:/
我只是看不到核心数据真正提供的任何帮助。约束功能以及被破坏的功能并没有真正发挥作用。当然,在所有现实世界的情况下,您只需要检查一个是否已经存在,如果存在则使用那个(例如,作为另一个项目的关系字段,当然)。我只是看不到任何其他方法。
为了节省任何人的输入...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // you've download 100 new guys from the endpoint, and unwrapped the json for guy in guys { // guy.id uniquely identifies let g = guy.id let r = NSFetchRequest<NSFetchRequestResult>(entityName:"CD_Guy") r.predicate = NSPredicate(format:"id == %d", g) var found: [CD_Guy] = [] do { let f = try core.container.viewContext.fetch(r) as! [CD_Guy] if f.count > 0 { continue } // that's it. it exists already } catch { print("basic db error. example, you had = instead of == in the pred above") continue } CD_Guy.make(from: guy) // just populate the CD_Guy save here: core.saveContext() } or save here: core.saveContext() |
请注意,在示例中,您可以在每次添加新内容时保存上下文,或者之后一次全部保存。
(我发现表格/集合绘制速度如此之快,与 CD 结合使用,这真的无关紧要。)
(不要忘记 .privateQueueConcurrencyType )
请注意,此示例基本上不会显示您创建实体并在另一个上下文中写入,并且您必须使用
1 2 3 | let pmoc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) pmoc.parent = core.container.viewContext do { try pmoc.save() } catch { fatalError("doh \\(error)")} |
查看 Apple 文档以进行属性间验证。它描述了如何在查询整个数据库的同时验证特定的插入或更新操作。
我真的很喜欢@DoozMen 的方法!!
我认为这是做我需要做的最简单的方法。
这是我将它安装到我的项目中的方式:
以下代码循环绘制一个相当长的tableView,为每个表行保存一个对象到DB,并为每个对象设置各种对象属性,如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | NSFetchRequest *request = [[NSFetchRequest alloc] init]; request.entity = [NSEntityDescription entityForName:@"Obiettivo" inManagedObjectContext:self.managedObjectContext]; request.predicate = [NSPredicate predicateWithFormat:@"obiettivoID = %d", obTag]; NSError *executeFetchError = nil; results = [[self.managedObjectContext executeFetchRequest:request error:&executeFetchError] lastObject]; if (executeFetchError) { NSLog(@"[%@, %@] error looking up for tag: %i with error: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), obTag, [executeFetchError localizedDescription]); } else if (!results) { if (obbCD == nil) { NSEntityDescription *ent = [NSEntityDescription entityForName:@"Obiettivo" inManagedObjectContext:self.managedObjectContext]; obbCD = [[Obiettivo alloc] initWithEntity:ent insertIntoManagedObjectContext:self.managedObjectContext]; } //set the property that has to be unique.. obbCD.obiettivoID = [NSNumber numberWithUnsignedInt:obTag]; [self.managedObjectContext insertObject:obbCD]; NSError *saveError = nil; [self.managedObjectContext save:&saveError]; NSLog(@"added with ID: %@", obbCD.obiettivoID); obbCD = nil; } results = nil; |