【问题标题】:Creating a CoreData entity based on attribute evaluation基于属性评估创建 CoreData 实体
【发布时间】:2011-06-27 06:37:15
【问题描述】:

假设我有一个实体MeetingNote,它具有标准属性以及与另一个实体Tagone to many 关系。

MeetingNote 实例中,我想创建另一个名为Task 的实体的实例,但前提是meetingNote.tag.name == 'task'TRUE

您认为建模TaskMeetingNote 之间关系的正确方法是什么?还是应该存在关系,而我应该使用带有适当谓词的获取属性?

【问题讨论】:

    标签: iphone database-design core-data


    【解决方案1】:

    首先,Core Data 的真正目的不是持久化,而是创建模型-视图-控制器设计应用程序的模型层。这意味着 Core Data 首先是一个模型/模拟 API,其次是一个持久性 API。因此,Core Data 数据模型应该准确地表示现实世界对象、条件或事件的属性以及它们之间的关系。

    因此,当您着手构建数据模型时,您应该忘记 UI、数据源或任何其他实现细节,而只是尝试创建一个反映应用程序的真实对象、条件或事件的模型处理。

    其次,虽然数据模型处理实体如何相关,但它不处理为什么的逻辑 实体是相关的。该逻辑通常属于实体的自定义 NSManagedObject 子类的代码中。在这种情况下,实体关系的方式MeetingNote 实体与TaskTags 相关。 为什么是任何特定的MeetingNote 对象和任何Task 对象之间应该存在关系,前提是MeetingNote 对象与@987654327 有关系@ 名称为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,如果是,则添加或删除Task 对象实例的MeetingNote.tasks 关系。

    但是,这有一个明显的性能损失,即必须检查添加或删除的每个标签。更好的解决方案是将自定义添加到仅在 MeetingNote.tags.name' contains a value oftask 的确切条件下调用的一个点。

    假设您有以下限制:

    1. 一个MeetingNote 对象不能有一个相关的Task 对象而没有一个Tag object with name=="task"
    2. 如果MeetingNote 对象确实有一个Tag object with name=="task",它必须至少有一个相关的Task 对象。
    3. 如果MeetingNote 对象失去了与Tag object with name=="task" 的关系,那么它就失去了所有的任务。

    此时很明显,`Tag object with name=="task" 是一个特殊的对象,其行为不同于其他标签。这证明并要求它有自己的实体和子类,因此我们将添加到数据模型中:

    TaskTag:Tag{
    }
    

    由于TaskTag 实体继承自Tag 实体,它可以自动继承Tag.meetingNotes 关系中的

    然后在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 对象的某些属性自动设置其名称。

    现在您已经了解了数据模型中的所有为什么逻辑,并且您的约束会自动执行。这可能不是您需要的确切解决方案,但您明白了。

    【讨论】:

    • 有这个例子真的很好。我可以要求额外解释一下这里需要 keyValue 观察,用 willChangeValueForKey 和 didChangeValueForKey 括起来吗?为什么self.meetingNotes = changedObjects; 不这样做?谢谢。
    • 不确定,说实话,因为我已经有一段时间没有尝试过了。我见过的所有 Apple 代码都获取了现有的可变集合对象,然后对其进行了更改。如果您只是换掉整个集合,我认为您不会获得所需的所有 KVO 通知。如果没有别的,引用集中的对象将不会收到任何 KVO 通知,因为它们没有任何变化。我认为甚至不需要 replaceAnEntireSet 通知来确保所有相关对象的所有 KVO 都正常工作。
    • 感谢您的信息。不知道更好,简单的集合分配是我实际用于我当前项目的,到目前为止,我的表格和复选框正在正确更新并且数据仍然存在 - 但现在我知道如果出现问题首先要怀疑什么。感谢您的解释。
    • 哇,这比我预期的要多,创建一个 TagTask 实体作为 Tag 的子类是完全有意义的。目前,我仍在为整个数据库建模而苦苦挣扎,但是当我终于开始编写代码时,您的见解将非常有用。出于好奇,您通常会在进行统计编码之前对您的对象和关系进行最终建模,还是您选择与代码实现同时进行的事情?再次感谢,非常感谢您的帮助。
    • @Rog -- 我认为在编写任何代码之前确定数据模型是一种很好的做法。数据模型是您应用程序的实际逻辑内容,其他一切都只是一种或另一种界面形式。理想情况下,您的数据模型应该可用于任何类型的界面,例如 GUI、命令行或网页。根据我的经验,一旦数据模型层完成,您就拥有了 75% 的应用程序,其余部分自然而然地就位。
    【解决方案2】:

    有趣的问题。我在这里的经验有限,但我忍不住尝试回答:

    如果您希望对 MeetingNote 进行的大量编辑需要立即反映在 Task 中,反之亦然,则关系将自动保持加载的对象相互更新。 (我基于 Richard Stahl 的帖子:fetched properties vs. relationships。)否则,获取的属性可能会更有效地进行故障排除。

    但是你为什么要把MeetingNote和Tag之间的关系设置为一对多呢?这意味着一个标签只能有一个 MeetingNote。对?因此,每当一个会议笔记被标记为“任务”时,就必须创建一个单独的标签。多对多不是更好吗?

    然后,如果走关系路线,您将在 MeetingNote 和 Task 之间建立一对一的关系。即使您希望多个会议笔记共享一个任务,因为该任务必须直接从会议笔记派生,所以无论如何您都将创建单独的任务。由于您为每个带有任务标签的 meetingNote 创建一个任务实例,因此这些任务不应有多个 meetingNote 关系,因为这会造成令人困惑的重复。

    【讨论】:

    • +1 一对一的好消息Tag--&gt;MeettingNote。当我阅读父母时,我错过了。
    • +1 感谢您的回答,您对 MeetingNote x Tag 关系的看法是正确的。这个想法是一个会议记录可以有很多标签,一个标签可以有很多与之关联的会议记录,这意味着在任何时间点我都可以查询标签task并获取所有已分配的会议记录实例task 标签。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-19
    • 2018-12-07
    • 1970-01-01
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多