基于属性评估创建核心数据实体

时间:2011-06-27 06:37:16

标签: iphone database-design core-data

假设我有一个实体MeetingNote,其中包含标准属性以及与另一个实体one to many的{​​{1}}关系。

Tag个实例中,我想创建另一个名为MeetingNote的实体的实例,但仅在TaskmeetingNote.tag.name == 'task'的情况下。

您认为建立TRUETask之间关系的正确方法是什么?或者应该有一个关系,我应该使用带有适当谓词的fetched属性?

2 个答案:

答案 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实体与TaskTags相关联。 原因 是指任何特定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任务的确切条件时调用的一个点。

假设您有以下约束:

  1. 如果没有MeetingNoteTask对象就无法拥有相关的Tag object with name=="task"对象。
  2. 如果MeetingNote对象确实有Tag object with name=="task",则必须至少有一个相关的Task对象。
  3. 如果MeetingNote对象失去与Tag object with name=="task"的关系,则会丢失其所有任务。
  4. 此时很明显,`具有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对象的某些属性自动设置其名称。

    现在,您拥有数据模型中的所有 原因 逻辑,并自动强制执行约束。这可能不是您需要的确切解决方案,但您明白了。