核心数据模型设计 [英] Core Data Model Design

查看:145
本文介绍了核心数据模型设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们假设我有一个关于烹饪食谱的应用程序,它有两个基本特征:


  1. 第一个涉及CURRENT食谱


  2. 第二个存储我已决定保存的食谱
  3. > STANDARD SCENARIO



    我当前的食谱是Cheese Cake和 RecipeDetailViewController 我为此食谱添加的当前食材:





    • 牛奶



    $ b

    好吧,



    * 点击保存 *

    / p>

    现在保存配方(现在已记录),并在 RecipesHistoryViewController 我可以看到这样:




    • 2013年11月15日 - 奶酪蛋糕

    • 2013年11月11日 - 布朗尼

    • 现在,如果我想要编辑历史,并将牛奶改为豆浆。



      问题是编辑历史记录中的食谱不应该在我的当前食谱中编辑食谱(及其成分),反之亦然。如果我编辑当前食谱并用花生酱替换黄油,则不得编辑历史记录中存储的任何食谱。希望我解释自己。



      后果



      目前,为了满足这个功能的功能,我复制食谱和每个子关系(成分),每次用户点击保存食谱按钮。它工作,但我觉得它可以是别的更干净。使用这个实现,结果是我有TONS不同的重复的核心数据对象(sqlite行)像这样:




      • 对象#1,名称:黄油,食谱:1

      • 对象#2,名称:黄油,食谱:4



      等。



      想法?如何优化此模型结构?



      编辑1



      已经考虑过创建任何 RecipeHistory 对象与属性 NSString 其中我可以存储一个json字典,但我不知道它是否更好。



      编辑2



      目前 RecipeHistory object包含以下内容:

        +  -  RecipeHistory  -  + 
      | |
      |属性:|
      | - date |
      + ------------------- +
      |关系:
      | - recipes |
      + ------------------- +

      + -----配方------ +
      |关系:
      | - recipeInfo |
      | - recipeshistory |
      | - 成分|
      + ------------------- +

      + - RecipeInfo ---- +
      | |
      |属性:|
      | - name |
      + ------------------- +

      + ---成分---- +
      | |
      |属性:|
      | - name |
      + ------------------- +
      |关系:
      | - recipe |
      + ------------------- +


      $ b当我创建一个 RecipeHistory 配方对象(及其关系RecipeInfo和Ingredients)b

      paulrehkugler c $ c>将填充数据库与大量的数据,但我没有找到另一个解决方案,让我以后的灵活性。也许在将来我会创建关于食谱和历史的统计数据,并且Core Data对象可以被证明是有用的。你有什么感想?我认为这是存储历史记录并允许编辑历史记录项的许多应用程序中的常见情况。






      BIG UPDATE < h2>

      我已经阅读了一些用户的答案,我想更好地解释这种情况。
      上面提到的例子只是一个例子,我的意思是我的应用程序不涉及厨师/食谱参数,但我已经使用食谱,因为我认为这是很好的我的真实情况。



      我想说明应用程序 NEEDS 两个部分:
      - 第一:我可以看到CURRENT食谱和相关配料
      - 第二:我可以在第一部分点击保存食谱按钮,决定保存的食谱



      在第一部分找到的当前食谱和X在历史部分找到的食谱没有共同的没有。但是,用户可以编辑保存在历史记录部分中的任何食谱(他可以编辑名称,配料,任何他想要的内容,他可以完全编辑历史记录部分找到的食谱)。



      这是为什么我复制所有 NSManagedObject 的原因。然而,以这种方式,数据库将成长为疯狂,因为每当用户保存当前配方时,表示配方(配方)的对象被复制,并且配方所具有的关系(配料)。所以会有TONS的成分命名为'黄油'例如。你可以说我:为什么你需要有TONS的'黄油'对象?嗯,我需要它,因为成分有例如quantity属性,所以每个食谱都有不同数量的成分。



      无论如何我不喜欢这种方法,即使它似乎是唯一的。



      PS:对不起,我的基本英语。



      EDIT



      解决方案

      由于您必须处理历史记录,并且由于事件是由最终用户手动生成的,请考虑改变方法:不是存储模型实体的当前视图(即配方,成分以及它们之间的连接)存储由用户发起的各个事件。这被称为



      这个想法是记录用户做什么,而不是在用户的操作之后记录新的状态。当您需要获取当前状态时,重放事件,将更改应用于内存中结构。除了允许您实现即时要求之外,您还可以通过重播直到某个日期的事件来恢复特定日期的状态。



      您可以通过定义以下事件来执行此操作:




      • CreateIngredient - 添加新成分,并为其赋予唯一的ID。

      • UpdateIngredient - 更改现有成分的属性。

      • DeleteIngredient - 从当前状态删除成分。

      • CreateRecipe - 添加新配方,并为其提供唯一的ID。

      • UpdateRecipeAttribute - 更改现有食谱的属性。

      • AddIngredientToRecipe - 向现有配方添加原料。

      • DeleteIngredientFromRecipe - 从现有配方中删除原料

      • DeleteRecipe - 删除食谱。
      • - 从特定食谱创建新的食谱历史记录,并为历史记录提供一个新ID。
      • UpdateRecipeHistoryAttribute - 更新特定食谱历史记录的属性。

      • AddIngredientToRecipeHistory - 将食谱添加到配方历史记录中。 b $ b
      • DeleteIngredientFromRecipeHistory - 从配方历史记录中删除配料。



      您可以使用Core Data API将单个事件存储在单个表中。添加一个按顺序处理事件的类,并创建模型的当前状态。事件将来自两个位置 - 由Core Data支持的事件存储器和用户界面。这将允许您保留单个事件处理器和单个模型,其中包含配方,配料和配方历史的当前状态的详细信息。


      只有当用户查阅历史记录时,才应重新播放事件。


      不,这不会发生:将整个历史记录启动到当前的视图,然后将新的事件发送到视图和DB的持久性。



      当用户需要查询历史(具体来说,当他们需要了解模型在过去的某个特定日期的样子时),您需要部分重放事件,直到感兴趣的日期为止。



      由于事件是手动生成的,所以不会有太多的事情:我估计最多的成千上万的计数 - 这是100个食谱的列表,每个10个成分。在现代硬件上处理事件应该以微秒为单位,因此读取和重放整个事件日志应该在几毫秒内。


      你知道任何链接,显示一个示例如何使用核心数据应用程序中的事件采购? [...]例如,我需要摆脱RecipeHistory NSManagedObject吗?


      我不知道一个好的参考实现用于iOS上的事件源。这与在其他系统上实现它不会太不同。您需要删除您当前拥有的所有表,将其替换为单个表,如下所示:





      属性如下:




      • EventId - 此事件的唯一ID。

      • EntityId - 此事件创建或修改的实体的唯一ID。此ID由创建... 处理器自动分配,且不会更改。

      • EventType - 表示此事件类型名称的短字符串。

      • EventTime - 事件发生的时间。

      • EventData - 事件的序列化表示 - 可以是二进制或文字。



        • 最后一个项目可以替换为代表上述12种事件类型使用的属性超集的反规范化的一组列。这完全取决于你 - 这个表只是存储你的事件的一种可能的方式。它不必是核心数据 - 事实上,它甚至不需要在数据库(虽然它使事情更容易一些)。


          Let's assume I have an app about cooking recipes with two fundamental features:

          1. The first one involves the CURRENT recipe that I'm preparing
          2. The second one stores the recipes that I've decided to save

          STANDARD SCENARIO

          My current recipe is "Cheese Cake" and in RecipeDetailViewController I can see the current ingredients I've added for this recipe:

          • Sugar
          • Milk
          • Butter
          • etc.

          Well, let's say that I'm satisfied from the final result and I decide to save (to log) the recipe I've just prepared.

          * click save *

          The recipe is now saved (is now logged) and in RecipesHistoryViewController I can see something like this:

          • Nov 15, 2013 - Cheese Cake
          • Nov 11, 2013 - Brownie
          • etc.

          Now if I want I can edit the recipe in the history and change Milk to Soy Milk, for example.

          The issue it's that editing the recipe in the history SHOULDN'T edit the recipe (and its ingredients) in my current recipe and vice versa. If I edit the current recipe and replace Butter with Peanut Butter it must not edit anyone of the recipe stored in history. Hope I explained myself.

          CONSEQUENCES

          What this scenario implies? Implies that currently, for satisfing the function of this features, I'm duplicating the recipe and every sub-relationship (ingredients) everytime the user click on "Save Recipe" button. Well it works but I feel it can be something else more clean. With this implemention it turns out that I have TONS of different duplicates Core Data object (sqlite rows) like these:

          • Object #1, name: Butter, recipe: 1
          • Object #2, name: Butter, recipe: 4
          • Object #3, name: Butter, recipe: 3

          etc.

          Ideas? How can I optimize this model structure?

          EDIT 1

          I've already thought of creating any RecipeHistory object with an attribute NSString where I could store a json dictionary but I don't know if it's better or not.

          EDIT 2

          Currently a RecipeHistory object contains this:

          +-- RecipeHistory --+
          |                   |
          | attributes:       |
          | - date            |
          +-------------------+
          | relationships:    |
          | - recipes         |
          +-------------------+
          
          +----- Recipe ------+
          | relationships:    |
          | - recipeInfo      |
          | - recipeshistory  |
          | - ingredients     |
          +-------------------+
          
          +-- RecipeInfo  ----+
          |                   |
          | attributes:       |
          | - name            |
          +-------------------+
          
          +--- Ingredient ----+
          |                   |
          | attributes:       |
          | - name            |
          +-------------------+
          | relationships:    |
          | - recipe          |
          +-------------------+
          

          paulrehkugler is true when he says that duplicating every Recipe object (and its relationships RecipeInfo and Ingredients) when I create a RecipeHistory is going to fill the database with a tons of data but I don't find another solution that allows me flexibility for the future. Maybe in the future I would to create stats about recipes and history and having Core Data objects could prove to be useful. What do you think? I think this is a common scenario in many apps that store history and allow to edit history item.


          BIG UPDATE

          I have read the answers from some users and I want to explain better the situation. The example I stated above is just an example, I mean that my app doesn't involve cook/recipe argument but I have used recipes because I think it's pretty okay for my real scenario.

          Said this I want to explain that the app NEEDS two sections: - First: where I can see the CURRENT recipe with related ingredients - Second: where I can see the recipe I decided to save by tapping a button 'Save Recipe' in the first section

          The current recipe found in the first section and a X recipe found in the 'history' section doesn't have NOTHING in common. However the user can edit whatever recipes saved in 'history' section (he can edit name, ingredients, whatever he wants, he can completely edit all things about a recipe found in history section).

          This is the reason why I came up duplicating all NSManagedObjects. However, in this way, the database will grow as mad because everytime the user saves the current recipe the object representing the recipe (Recipe) is duplicated and also the relationships the recipes had (ingredients). So there will be TONS of ingredients named 'Butter' for example. You can say me: why the hell you need to have TONS of 'Butter' objects? Well, I need it because ingredients has for example the 'quantity' attribute, so every recipe have ingredients with different quantities.

          Anyhow I don't like this approach, even it seems to be the only one. Ask me whatever you want and I'll try to explain every detail.

          PS: Sorry for my basic English.

          EDIT

          解决方案

          Since you must deal with history, and because the events are generated manually by end users, consider changing the approach: rather than storing the current view of the model entities (i.e. recipes, ingredients, and the connections among them) store the individual events initiated by the user. This is called Event Sourcing.

          The idea is to record what user does, rather than recording the new state after the user's action. When you need to get the current state, "replay" the events, applying the changes to in-memory structures. In addition to letting you implement the immediate requirements, this would let you restore the state as of a specific date by "replaying" the events up to a certain date. This helps with audits.

          You can do it by defining events like this:

          • CreateIngredient - Adds new ingredient, and gives it a unique ID.
          • UpdateIngredient - Changes an attribute of an existing ingredient.
          • DeleteIngredient - Deletes an ingredient from the current state. Deleting an ingredient deletes it from all recipes and recipe histories.
          • CreateRecipe - Adds a new recipe, and gives it a unique ID.
          • UpdateRecipeAttribute - Changes an attribute of an existing recipe.
          • AddIngredientToRecipe - Adds an ingredient to an existing recipe.
          • DeleteIngredientFromRecipe - Deletes an ingredient from an existing recipe.
          • DeleteRecipe - Deletes a recipe.
          • CreateRecipeHistory - Creates a new recipe history from a specific recipe, and gives the history a new ID.
          • UpdateRecipeHistoryAttribute - Updates an attribute of a specific recipe history.
          • AddIngredientToRecipeHistory - Adds an ingredient to a recipe history.
          • DeleteIngredientFromRecipeHistory - Deletes an ingredient from a recipe history.

          You can store the individual events in a single table using Core Data APIs. Add a class that processes events in order, and creates the current state of the model. The events will come from two places - the event store backed by Core Data, and from the user interface. This would let you keep a single event processor, and a single model with the details of the current state of recipes, ingredients, and recipe histories.

          Replaying the events should happen only when the user consults the history, right?

          No, that is not what happens: you read the whole history on start-up into the current "view", and then you send the new events both to the view and to the DB for persistence.

          When users need to consult the history (specifically, when they need to find out how the model looked as of a specific date in the past) you need to replay the events partially, up until the date of interest.

          Since the events are generated by hand, there wouldn't be too many of them: I would estimate the count in the thousands at the most - that's for a list of 100 recipes with 10 ingredients each. Processing an event on a modern hardware should be in microseconds, so reading and replaying the entire event log should be in the milliseconds.

          Furthermore, do you know any link that shows an example of how to use Event Sourcing in a Core Data application? [...] For example, should I need to get rid of RecipeHistory NSManagedObject?

          I do not know of a good reference implementation for event sourcing on iOS. That wouldn't be too different from implementing it on other systems. You would need to get rid of all tables that you currently have, replacing it with a single table that looks like this:

          The attributes would be as follows:

          • EventId - Unique ID of this event. This is assigned automatically on insertion, and never changes.
          • EntityId - Unique ID of the entity created or modified by this event. This ID is assigned automatically by a Create... processor, and never changes.
          • EventType - A short string representing the name of this event type.
          • EventTime - The time the event has happened.
          • EventData - A serialized representation of the event - this can be binary or textual.

          The last item can be replaced for a "denormalized" group of columns representing a superset of attributes used by the 12 event types above. This is entirely up to you - this table is merely one possible way of storing your events. It does not have to be Core Data - in fact, it does not even need to be in a database (although it makes things a little easier).

          这篇关于核心数据模型设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆