假设我有一个实体MeetingNote
,其中包含标准属性以及与另一个实体one to many
的{{1}}关系。
在Tag
个实例中,我想创建另一个名为MeetingNote
的实体的实例,但仅在Task
为meetingNote.tag.name == 'task'
的情况下。
您认为建立TRUE
和Task
之间关系的正确方法是什么?或者应该有一个关系,我应该使用带有适当谓词的fetched属性?
答案 0 :(得分:2)
有趣的问题。我在这里的经历是有限的,但我无法抗拒尝试答案:
如果您希望对MeetingNote进行大量编辑,需要立即反映在Task中,反之亦然,则关系会自动保持加载的对象相互更新。 (我的理由基于Richard Stahl的帖子:fetched properties vs. relationships。)否则,获取的属性可能会更有效率。
但是你为什么要把MeetingNote和Tag之间的关系作为一对多呢?这意味着标签只能有一个MeetingNote。对?因此,只要会议标记被标记为“任务”,就必须创建单独的标记。难道多对多会不会更好?
然后,如果走了关系路线,你会在MeetingNote和Task之间建立一对一的关系。即使您期望多个meetingNotes共享任务,由于任务必须直接从meetingNote派生,因此您无论如何都将创建单独的任务。而且,由于您要为每个任务标记的meetingNote创建一个任务实例,因此这些任务不应具有多个meetingNote关系,因为这会产生令人困惑的重复。
答案 1 :(得分:2)
首先,Core Data的真正目的不是持久性,而是创建模型 - 视图 - 控制器设计应用程序的模型层。这意味着Core Data首先是模型/模拟API,其次是持久性API。因此,核心数据数据模型应准确地表示现实世界对象,条件或事件的属性以及它们之间的关系。
因此,当您开始构建数据模型时,您应该忘记UI,数据源或任何其他实现细节,只需尝试创建一个模拟应用程序的真实对象,条件或事件的模型对付。
其次,虽然数据模型处理 实体如何相关,但它不处理 为什么的逻辑实体是相关的。该逻辑属于代码,通常位于实体的自定义NSManagedObject子类中。在这种情况下,实体关系的 是MeetingNote
实体与Task
和Tags
相关联。 原因 是指任何特定MeetingNote
对象与任何Task
对象之间应该存在关系,仅当MeetingNote
对象具有与Tag
对象的关系,其名称为task
。
因此,您的基本数据模型应如下所示:
MeetingNote{
title:string
date:date
tags<<-->>Tag.meetingNotes
tasks<-->>Task.meetingNote
}
Task{
name:string
meetingNote<<-->MeetingNote.tasks
}
Tag{
name:string
meetingNotes<<-->>MeetingNote.tags
}
现在问题变成了 why 的自定义逻辑的位置之一。最简单的方法是为MeetingNote.tags
属性创建一个自定义访问器,检查添加或删除MeetingNote instance
的标记的名称是否等于task
,如果是,则添加或删除实例的Task
关系中的MeetingNote.tasks
对象。
但是,这必须检查添加或删除的每个标记,这显然会降低性能。更好的解决方案是将自定义添加到仅在MeetingNote.tags.name' contains a value of
任务的确切条件时调用的一个点。
假设您有以下约束:
MeetingNote
,Task
对象就无法拥有相关的Tag object with name=="task"
对象。 MeetingNote
对象确实有Tag object with name=="task"
,则必须至少有一个相关的Task
对象。MeetingNote
对象失去与Tag object with name=="task"
的关系,则会丢失其所有任务。 此时很明显,`具有name ==“task”的Tag对象是一个特殊对象,其行为与其他标签不同。这证明并且要求它有自己的实体和子类,因此我们将添加到数据模型中:
TaskTag:Tag{
}
由于TaskTag
实体继承自Tag
实体,因此它可以自动继承Tag.meetingNotes
关系,因此从任何角度看它都将作为Tag
对象运行MeetinNote
个对象。
然后在TaskTag
NSManagedObject子类中添加以下代码:
-(NSString *) name {
// the name of a TaskTag is always "task"
// you should set the defalut value in the data model to "task" as well.
return @"task";
}
-(void) setName:(NSString *)name{
return; // the name can never be changed
}
- (void)addMeetingNotesObject:(MeetingNote *)value {
NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
[self willChangeValueForKey:@"meetingNotes" withSetMutation:NSKeyValueUnionSetMutation usingObjects:changedObjects];
[[self primitiveValueForKey:@"meetingNotes"] addObject:value];
// If the meeting object does not an existing task, add one
if ([value.tasks count]==0 ) {
Task *t=[NSEntityDescription insertNewObjectForEntityForName:@"Task" inManagedObjectContext:self.managedObjectContext];
t.meetingNote=value;
}
[self didChangeValueForKey:@"meetingNotes" withSetMutation:NSKeyValueUnionSetMutation usingObjects:changedObjects];
[changedObjects release];
}
- (void)removeMeetingNotesObject:(MeetingNote *)value {
NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
[self willChangeValueForKey:@"meetingNotes" withSetMutation:NSKeyValueMinusSetMutation usingObjects:changedObjects];
[[self primitiveValueForKey:@"meetingNotes"] removeObject:value];
// A MeetingNote object cannot have any task without a taskTag so remove all task objects
if ([value.tasks count]!=0 ) {
[value removeTasks:value.tasks]; // removes all tasks from meeting notes
}
[self didChangeValueForKey:@"meetingNotes" withSetMutation:NSKeyValueMinusSetMutation usingObjects:changedObjects];
[changedObjects release];
}
- (void)addMeetingNotes:(NSSet *)value {
[self willChangeValueForKey:@"meetingNotes" withSetMutation:NSKeyValueUnionSetMutation usingObjects:value];
[[self primitiveValueForKey:@"meetingNotes"] unionSet:value];
Task *newTask;
// same as addMeetingNotesObject:
for (MeetingNote *meetNote in value) {
if ([meetNote.tasks count]==0 ) {
newTask=[NSEntityDescription insertNewObjectForEntityForName:@"Task" inManagedObjectContext:self.managedObjectContext];
newTask.meetingNote=value;
}
}
[self didChangeValueForKey:@"meetingNotes" withSetMutation:NSKeyValueUnionSetMutation usingObjects:value];
}
- (void)removeMeetingNotes:(NSSet *)value {
[self willChangeValueForKey:@"meetingNotes" withSetMutation:NSKeyValueMinusSetMutation usingObjects:value];
[[self primitiveValueForKey:@"meetingNotes"] minusSet:value];
//removeMeetingNotesObject:
for (MeetingNote *meetNote in value) {
[meetNote removeTasks:meetNote.tasks];
}
[self didChangeValueForKey:@"meetingNotes" withSetMutation:NSKeyValueMinusSetMutation usingObjects:value];
}
// Note: This code is not compiled and my contain errors.
此代码将自动强制执行上述约束,而无需执行任何其他操作。您还可以自定义Task
子类,以根据与其相关的MeetingNote
对象的某些属性自动设置其名称。
现在,您拥有数据模型中的所有 原因 逻辑,并自动强制执行约束。这可能不是您需要的确切解决方案,但您明白了。