Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does one architect an entity in Core Data with a generic relationship?

Say you need to architect an app with an entity that can be associated with multiple other kinds of entities. For example, you have a Picture entity that can be associated with a Meal entity, a Person entity, a Boardroom entity, a Furniture entity, etc. I can think of a number of different ways to address this problem, but -- perhaps because I'm new to Core Data -- I'm not comfortable with any of them.

The most obvious approach that comes to mind is simply creating a relationship between Picture and each entity that supports associated pictures, but this seems sloppy since pictures will have multiple "null pointers."

Another possibility is creating a superentity -- Pictureable -- or something. Every entity that supports associated pictures would be a subentity of Pictureable, and Picture itself would have a one-to-one with Pictureable. I find this approach troubling because it can't be used more than once in the context of a project (since Core Data doesn't support multiple inheritance) AND the way Core Data seems to create one table for any given root entity -- assuming a SQLite backing -- has me afeard of grouping a whole bunch of disparate subentities under the umbrella of a common superentity (I realize that thinking along these lines may smack of premature optimization, so let me know if I'm being a ninny).

A third approach is to create a composite key for Picture that consists of a "type" and a "UID." Assuming every entity in my data model has a UID, I can use this key to derive an associated managed object from a Picture instance and vice versa. This approach worries me because it sounds like it might get slow when fetching en masse; it also doesn't feel native enough to me.

A fourth approach -- the one I'm leaning towards for the app I'm working on -- is creating subentities for both Picture and X (where X is either Meal, Person, Boardroom, etc.) and creating a one-to-one between both of those subentities. While this approach seems like the lesser of all evils, it still seems abstruse to my untrained eye, so I wonder if there's a better way.

Edit 1: In the last paragraph, I meant to say I'm leaning towards creating subentities just for Picture, not both Picture and X.

like image 562
Joshua Pokotilow Avatar asked Jul 07 '11 18:07

Joshua Pokotilow


People also ask

How do I create a Core Data entity?

To create an entityClick Add Entity. A new untitled entity appears in the Entities list in the navigator area. Select the new untitled entity. In the Entity pane of the Data Model inspector, enter the name of the entity, and press Return.

How do you define relationships between entities?

The relationship between two entities can often be expressed with a verb. For example, the relationship between the DEPARTMENT entity and the EMPLOYEE entity might be expressed with the verb phrase BELONGS TO. An employee belongs to a department in an organization.

Is it possible to have relationship in Core Data?

Inverse relationships enable Core Data to propagate change in both directions when an instance of either the source or destination type changes. Every relationship must have an inverse. When creating relationships in the Graph editor, you add inverse relationships between entities in a single step.


2 Answers

I think the best variations on this theme are (not necessarily in order):

  1. Use separate entities for the pictures associated with Meal, Person, Boardroom, etc. Those entities might all have the same attributes, and they might in fact all be implemented using the same class. There's nothing wrong with that, and it makes it simple to have a bidirectional relationship between each kind of entity and the entity that stores its picture.

  2. Make the picture an attribute of each of the entity types rather than a separate entity. This isn't a great plan with respect to efficiency if you're storing the actual picture data in the database, but it'd be fine if you store the image as a separate file and store the path to that file in an attribute. If the images or the number of records is small, it may not really be a problem even if you do store the image data in the database.

  3. Use a single entity for all the pictures but omit the inverse relationship back to the associated entity. There's a helpful SO question that considers this, and the accepted answer links to the even more helpful Unidirectional Relationships section of the docs. This can be a nice solution to your problem if you don't need the picture->owner relationship, but you should understand the possible risk before you go down that road.

  4. Give your picture entity separate relationships for each possible kind of owner, as you described in the first option you listed. If you'll need to be able to access all the pictures as a group and you need a relationship from the picture back to its owner, and if the number of possible owner entities is relatively small, this might be your best option even if it seems sloppy to have empty attributes.

As you noticed, when you use inheritance with your entities, all the sub-entities end up together in one big table. So, your fourth option (using sub-entities for each kind of picture) is similar under the hood to your first option.

Thinking more about this question, I'm inclined toward using entity inheritance to create subentities for the pictures associated with each type of owner entity. The Picture entity would store just the data that's associated with any picture. Each subentity, like MealPicture and PersonPicture, would add a relationship to it's own particular sort of owner. This way, you get bidirectional Meal<->MealPicture and Person<->PersonPicture relationships, and because each subentity inherits all the common Picture stuff you avoid the DRY violation that was bugging you. In short, you get most of the best parts of options 1 and 3 above. Under the hood, Core Data manages the pictures as in option 4 above, but in use each of the picture subentities only exposes a single relationship.

like image 143
Caleb Avatar answered Sep 19 '22 07:09

Caleb


Just to expand a bit on Caleb's excellent summation...

I think it's important not to over emphasize the similarities between entities and classes. Both are abstractions that help define concrete objects but entities are very "lightweight" compared to classes. For one thing, entities don't have behaviors but just properties. For another, they exist purely to provide other concrete objects e.g. managed object context and persistent stores, a description of the data model so those concrete objects can piece everything together.

In fact, under the hood, there is no NSEntity class, there is only an NSEnitity***Description*** class. Entities are really just descriptions of how the objects in an object graph will fit together. As such, you really don't get all the overhead an inefficiency of multiplying classes when you multiply entities e.g. having a bunch of largely duplicate entities doesn't slow down the app, use more memory, interfere with method chains etc.

So, don't be afraid to use multiple seemingly redundant entities when that is the simplest solution. In Core Data, that is often the most elegant solution.

like image 43
TechZen Avatar answered Sep 23 '22 07:09

TechZen