I have two entities that are connected through a one-to-many relationship, let's say CategoryEntity and ItemEntity. The relationship is optional for CategoryEntity (there can be categories without items), but required for every ItemEntity. At the app's loading, the Categories already exist in the store, and I want to import ItemEntities and connect them to the appropriate CategoryEntity.
Obviously executing a FetchRequest for each ItemEntity to find the matching category wouldn't be a good solution because there will be about 4000-6000 Items each time..
So, is there something more efficient I could do?
If you have correctly setup your Core Data model, then you have a to-many relationship from the Category entity to the Item entity, and an inverse to-one relationship from Item to Category. Also, you have a "cascade" delete rule for the to-many relationship and a "nullify" delete rule for the to-one relationship.
Assuming this, each time you insert an Item object, setting its Category relationship automatically inserts the Item into the corresponding Category. Deleting an Item automatically removes it from the corresponding Category.
On the Category side, removing a Category automatically removes all of the corresponding Item objects.
Therefore, when you fetch Items, you have already fetched for each Item object its corresponding Category object. You do not need do anything else. Note that, by default, you are not actually retrieving the Category object during the fetch: instead a fault is fired the first time you try to access the object and the object is retrieved at that time. This provides better performances if you do not plan to use immediately the Category object stored within the Item object just fetched. If you plan to use the Category object almost every time you fetch an Item, then you must use the NSFetchRequest methods
- (void)setReturnsObjectsAsFaults:(BOOL)yesNo
- (void)setRelationshipKeyPathsForPrefetching:(NSArray *)keys
to tell Core Data that you do now want faults and that you ask for prefetching your Category relationship.
When you say 'import' item entities, what do you mean? Are these in another Core Data store, defined in another format in a file somewhere, retrieved over the network?
One approach would be to fetch all the categories in one go and add them to an NSDictionary acting as a cache and keyed by some identifying value that allows you to perform a quick lookup. For each item entity that you instantiate during import (whatever that means), retrieve its category ID and then retrieve the Category MO from the cache. Set the relationship and then save. Even better, batch up a number of insertions and save every 10, 100 or 1000 to reduce IO overhead.
Related
We built a mechanism that on server request automatically retrieves the entities list from the DB, and maps them to ViewModels. We can't figure out how to support clearing View Models that are built from multiple DB entities when an update was made to some relevant DB entity.
This is how we've built our Cache:
We have a mapping between the key (the DB entity name) and the list of values it holds (View models).
This is how we handle entities invalidate:
On EF's Context Save method, we clear the Cache values by the saved entity's name (when it has been changed).
For most entities, all the Cache values are mapped to a single entity.
For some entities however, the Cache values depend on several other entities (which are either direct or indirect navigation properties of the entity).
For instance:
We have an EducationPlace Entity.
Its Cache key is "EducationPlace". Its values are a list of EducationPlaceViewModel.
EducationPlace entity has an Address property, and the Address entity, has a City property.
City's name is used in the EducationPlaceViewModel.
Now we want, that when the City entity is being updated, the Cache's EducationPlaceViewModel list will be cleared.
What we are looking for, is a way to update a cache entry, when an entity that the cache entry's View Model depends on is being updated.
The two solutions we've considered are:
Use reflection to find which entries should be cleared (by following the navigation properties). This will create many unnecessary dependencies, which will result in many redundant cache clears. For instance, EducationPlace has many navigation properties that are not being used by its View Model, hence should not trigger a Cache clear.
Use a custom Attribute that will decorate each relevant entity, and will hold its list of entities that its View Model depends on. This way we will easily get the entities that should be cleared when an entity is being saved at the Save method.
This way requires A LOT of maintenance. It requires for each new entity which its View Model depends on other entities for the developer to remember to use it on the new entity, and to update that entity when its View Model depends on new entities.
This is how we clear the View Models.
This method is invoked from the Context's Save method:
What this method basically does is:
It gets all the DB entities from the DAL layer
For each entity name, it:
a. Skips none-relevant entities
b. if the entity name exists as a Cache key - Clear its data
I've set up a Core Data mirroring to CloudKit using NSPersistentCloudKitContainer. My simplified data structure consists of List and Item. List have one-to-many relationship items with many Items, Item has parentList property.
When I am getting CKRecord associated with List NSManagedObject and sharing it, I expect all child Items to be shared too. I know, I can set parentRecord property for each CKRecord associated with the List
childRecord.setParent(parentRecord)
But I believe there is some smart way to avoid doing that manually, because everything is mirroring automatically and it looks stupid to start handling each single property manually to only set parentRecord.
Is there any way to do that for NSManagedObjects? Because my custom relationship does not mean for CloudKit that the List is parent for the Item.
My model have 2 Entity (Category and News) with many to many relationship: (It mean a category may have many news and a news article may belong to one or more Category).
Below is my design:
Entity 1:
Category (attributes: categoryID, title, show, position) and a relationship with News Entity called "news".
"news" relationship has destination is "News", inverse relationship is "categories", type to-many relationship and delete rule is cascade.
Entity 2:
News (attributes: newsID, quote, content, link) and a relationship with Category Entity called "categories".
"categories" Relationship has destination is "Category", inverse relationship is "news", type to-many relationship and delete rule is nullify.
Object graph look like this:
News <<-------------->> Category
My question is:
1> Is my designed model is good ?
2> Arcoding to my designed model, if I delete a News object from it context example like:
id newsObjectToDelete = ....
[managedObjectContext deleteObject:newsObjectToDelete];
Does Category object that newsObjectToDelete belong to automatically remove newsObjectToDelete from NSSet of relationship "News".
3> If I want to constraints that a "News" must belong to at least one "Category". How to implement that constraints
Thank in advance. Sorry for poor English
It's tough to say whether or not this is a good fit for your app, but it looks like a pretty standard relationship.
If you delete an object, it is automatically removed from all Core Data relationships. Don't worry about a dangling reference to some deleted object, Core Data handles that for you.
Core Data can really only generate an error (and blocking the save) or take some automatic action during the save. You can do either of the following:
In Your Data Model: Using the data model editor, uncheck "Optional" for this relationship, and/or set the "Minimum" count to 1:
In Your "News" Subclass: If you need more fine-grained control and/or error reporting, you can check this during validation (to generate an error) or maybe take some automatic actions during the save process.
Note that using validation (either in the model or in your code) is only going to help during the development and debugging processes - calling -save: on the managed object context will fail, returning NO and generating an error. This should be a last resort, and really just there to keep bad data from getting into your persistent store. A validation failure like this tells you that some other part of your code is wrong and generating bad objects.
I have set up Core data in my application, with two entities: Restaurant and Menu. There are about 30 or so restaurants, and 6 menus. One menu can belong to several restaurants.
However, each Restaurant entity has a menuId field. Is there a way to retrieve the specific menu entity according to which menuId the Restaurant entity has?
I also have a problem with values being stored more than once, even though they are the same. How can I prevent that from happening?
I'm pretty new to Core data, so any best-practice tips would be appreciated!
Thanks!
You could use an NSFetchRequest to retrieve menus by their id, but it would actually be better to define a relationship from your Restaurant entity to your Menu entity (and vice-versa). Internally, that does pretty much the same thing as your menu id attribute, but it's much more convenient to use.
You're following a database approach, based on tables and IDs to link these tables. This is wrong in Core Data, you must follow a object graph approach and CoreData will transform it to a corresponding SQlite database in a manner completely transparent to you.
So given the entities °Restaurant" and "Menu" you will create:
- in the entity "Restaurant" a 1-to-many relationship to "Menu" called "menus"
- in the entity "Menu" a 1-to-many relationship to "Restaurant" called "restaurants"
You will just need to add each restaurant menu to the NSSet corresponding to the menus relationship for a given menu, and vice versa. As you can see in this way you don't need to care about IDs, but just the object relationships.
As far as repeated data, this depends on you. Normally you should have a way to identify the unicity of an object (e.g.: the menu name, the restaurant name) and then try to retrieve it from Core Data before adding it as a new object.
I'm working on an table drill-down style iPhone app that has prepopulated data. I use Core Data and NSFetchedResultsController to populate the table views. In the last level of the table view which shows an item (managed object) I want my user to be able to select that item which should eventualy be shown in another view. That other view would be a kind of a favorite list (implemented in a tab view). The user would then have a choice of deleting or adding other items to the favorite list.
My model has three entities each representing one level of table view. Higher level entity has a to-many relationship to lower level entity and inverse relationships are to-one
How do I use the existing managed object (object in the last level of table view) to save it and show it in favorite list view? Should I create new entity and establish relationship between the two?
My model has three entities each
representing one level of table view.
That is thinking backwards. What you have is three entities that exist logically in a hierarchy and the hierarchy of views reflects that logical structure. The views exist to display the data, the data does not exist to display the views. It's an important concept to grasp and if you fail to do so, your application design will always be overly complex, fragile and hard to extend and maintain. The data model always comes first and the logical relationships within the data model itself and the users interactions with that data ultimately control the UI structure of the app.
It's an easy trap to fall into because the instructional materials always start with the interface first. However, in real app design, you start with the data model first and work forward to the interface.
In this case, if you want to store favorites of some entity you have two choices. If the entity being a favorite is part of the core relationship between data and the user and you only have one set of favorites, then you could legitimately add a boolean "isFavorite" attribute to the entity and then just fetch al the entities where "isFavorite==YES"; If you have multiple list of favorites then the best method would to to create a FavoritesList entity and then relate each favorites entity to the objects it is supposed to list.
If the favorites are a minor and peripheral part of the user's interaction with the data you could store the objectIDs in the user defaults.
Yes, you could create a new entity and store the relationship. It's not necessarily the only way to do it -- you could store pointers to your NSManagedObjects in a container like an NSMutableArray -- but if you want to remember that list for later (i.e. save it between launches), it might make the most sense to also store it using Core Data.