假设我有一个具有标准属性的实体MeetingNote
,加上与另一个实体Tag
的one to many
关系。
在MeetingNote
实例中,我想创建另一个名为Task
的实体的实例,但前提是meetingNote.tag.name == 'task'
是TRUE
。
你认为对Task
和MeetingNote
之间的关系建模的正确方法是什么?或者应该有一个关系,而我应该使用一个带有适当谓词的提取属性?
首先,核心数据的真正目的不是持久性,而是创建模型-视图-控制器设计应用程序的模型层。这意味着核心数据实际上首先是模型/模拟API,其次是持久性API。因此,核心数据数据模型应该准确地表示真实世界对象、条件或事件的属性以及它们之间的关系。
因此,当你着手构建数据模型时,你应该忘记UI、数据源或任何其他实现细节,只需尝试创建一个反映应用程序处理的真实世界对象、条件或事件的模型。
其次,虽然数据模型处理实体如何相关,但它不处理实体为什么相关的逻辑。该逻辑属于代码中,通常位于实体的自定义NSManagedObject子类中。在这种情况下,实体关系的how是MeetingNote
实体与Task
和Tags
都相关。的原因是,只有当MeetingNote
对象与名为task
的Tag
对象有关系时,任何特定的MeetingNote
对象和任何Task
对象之间才应该有关系。
所以,你的基本数据模型应该是这样的:
MeetingNote{
title:string
date:date
tags<<-->>Tag.meetingNotes
tasks<-->>Task.meetingNote
}
Task{
name:string
meetingNote<<-->MeetingNote.tasks
}
Tag{
name:string
meetingNotes<<-->>MeetingNote.tags
}
现在的问题是,为什么的自定义逻辑应该放在哪里。逻辑上最简单的方法是为MeetingNote.tags
属性创建一个自定义访问器,该访问器检查添加或删除到MeetingNote instance
的标记的名称是否等于task
,如果是,则从实例的MeetingNote.tasks
关系中添加或删除Task
对象。
然而,这会带来明显的性能损失,即必须检查添加或删除的每个标记。一个更好的解决方案是将自定义添加到只有当MeetingNote.tags.name' contains a value of
任务的确切条件为"时才调用的一个点。
假设您有以下限制:
- 一个
MeetingNote
对象如果没有Tag object with name=="task"
,就不能具有相关的Task
对象 - 如果
MeetingNote
对象确实具有Tag object with name=="task"
,则它必须至少具有一个相关的Task
对象 - 如果
MeetingNote
对象失去了与Tag object with name=="task"
的关系,那么它将失去所有任务
在这一点上很明显,`Tag object with name="task"是一个特殊的对象,其行为与其他标记不同。这证明并要求它有自己的实体和子类,因此我们将添加到数据模型中:
TaskTag:Tag{
}
由于TaskTag
实体继承自Tag
实体,因此它可以在Tag.meetingNotes
关系中自动继承,因此从任何MeetinNote
对象的角度来看,它将表现为Tag
对象。
然后在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
对象的某些属性自动设置其名称。
现在,您已经在数据模型中拥有了why的所有逻辑,并且您的约束将自动强制执行。这可能不是你需要的确切解决方案,但你已经明白了。
有趣的问题。我在这里的经验有限,但我忍不住尝试了一个答案:
如果您期望对MeetingNote进行大量编辑,这些编辑需要立即反映在Task中,反之亦然,则关系会自动使加载的对象相互更新。(我基于Richard Stahl在这里的帖子:提取的属性与关系。)否则,提取的属性可能会在错误方面更有效。
但你为什么要把MeetingNote和Tag之间的关系当作一对多呢?这意味着一个标签只能有一个MeetingNote。正确的因此,每当meetingNote被标记为"task"时,都必须创建一个单独的标记。多对多不是更好吗?
然后,如果按照关系路线,你会在MeetingNote和Task之间建立一对一的关系。即使您希望多个meetingNotes共享一个任务,由于该任务必须直接从meetingNote派生,因此您无论如何都要创建单独的任务。由于您要为每个标记为meetingNote的任务创建一个任务实例,因此这些任务不应该有多个meetingNote关系,因为这会造成令人困惑的重复。