I would like to know if there is some kind of similar functionality or way to preform an "on duplicate key update" function with core-data as there is with MySQL.
What I want to do is to save an object to the database every time a user presses a button. But if the button is already pressed I want to update the row with some new values instead of adding a new row.
The only way I currently know how to do this is to read the rows from the DB, see if the row exists and then update it.. otherwise add a new row. This seems kind of bad to do this way, am I wrong?
The easiest answer to this is to run a query against the Core Data context and get the object back if it exists. This is the most efficient and least error prone solution to the problem.
You do not need to create a separate NSManagedObjectContext and attempt to deal with merge policies, that is a very inefficient and dangerous way to try and resolve such a simple issue.
Core Data handles a lot of caching for you in the background. If you are attempting to retrieve an object that you just created there is a very high probability that it is still sitting in the cache so the response to your query will be nearly instantaneous.
Note
I just went back to both of those sample projects again to file a bug against them and noticed that they have been updated and finally removed the suggestion of creating a new context. They are now using the NSUndoManager where appropriate and a single context.
EDIT
Remember that the core data framework manages persistence of your object graph. It is not an interface to a sqlite database.
Worry about your object life cycle. When do instances get created? When are they destroyed? What makes your instances unique? Using Books as an example entity, a book has an ISBN which is a unique way of identifying a title, yet many copies of each title can exist. You have two choices in your Entity model, you can create separate instances for each copy of the title or have one instance with a count attribute.
The sample projects CoreDataBooks and iPhoneCoreDataRecipes use NSUndoManager to track state changes between views.
Related
I am having troubles with the relationship I have setup in CoreData. Its one to many, a table can have many people but only one person can sit at one table.
My app is based off Apples CoreDataBooks example here.
This does't handle relationships though. You see the 'EditingViewController.m' in the example project, I have this as well, its where the user edits and saves a change to a chosen attribute of a CoreData object.
So let's say they go and choose to edit the name attribute of my person entity. They get a text field where they enter a name and then tap the save button, done.
Now my person entity has a relationship with my table entity, but I can't get it saving correctly and it's likely that I don't fully understand how it works yet. So let's say they select to edit the table of that person, they get a UIPickerView howling a list of tables, great, I've gotten this far, I've used a fetch request to get the table objects and list them.
Normally setting a standard attribute, a line such as this would be called in my code:
[editedObject setValue:textField.text forKey:editedFieldKey];
However, this time I am using a relationship so this won't work, right?
So now my relationship is setup, I try this instead of the above:
[tableObject addGuestObject:(Person *)editedObject];
So from the fetch request that I filled my picker view with I work out the selected table and get that object, tableObject. Then as the CoreDataBooks example did for me already the editedObject has been passed down to this view and is the person in question that we are editing.
So this is where I am liking misunderstanding it. Surely as with the last line of code, we simply take the existing table object (that was selected) and then add the existing guest object to it?
But this gives me this error and crashes:
'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x145640 <x-coredata://2FAD62C1-382A-4398-A4CA-02B4B41DC9A5/Table/p2>''
Not entirely sure what to do to remedy this.
Generally the 'could not fulfil fault' means that the object was deleted out from the persistent store yet you still have a reference to the object. That could happen for a number of reasons (persistent store changed, object was deleted by another thread, object was owned by another object with a relationship which had cascade and it was deleted). Think we need a bit more info about the relationships from it.
I have a Answer table which has pk_answerid, answertext , fk_questionid, chosenoptions stored as 1,3,2 (comma separated)
answertext if populated, then chosenoptions null and if chosenoptions if populated then answertext is null.
ChosenOption table has structure pk_chosenoptionid, fk_questionid
In EntityFramework 4,
I have something along these lines
void SaveAnswers(ICollection<Answer> answers)
{
context.Answers.Add(answers);
context.SaveChanges();
}
This works fine....but when in the UI I go back to page which is wizard with Q&A's on different pages and I deselect all answers previously selected, and click save, no answers are deleted. I also tried DeleteObject which does not delete the Answers and chosenoptions even though I have cascade delete on the Answer table to Chosen option table.
Also in edit scenarios if for e.g. if the User selected option 1,2 and then saves it and then goes back and selects 3,2 how do you write code in EF to do such complex stuff. I haven't come across any tutorials which explain such scenarios. Most of what I have seen is simple add, delete and applypropertychanges. I have an MVC app, which has lazy loading enabled.
Pls help and suggest some sort of code using an example or any pointers to existing blogs where this is explained.
EF doesn't take care - I shame to posting this again and again but I really don't like to explain it every time: Update relationships when saving changes of EF4 POCO objects
Check that explanation. Your problem is similar. You persisted entity graph (set of related entities) and now you want to change the graph. Your new graph is extracted from web request which mean it is detached and EF doesn't know what has changed. You must do it manually - I just discussed what does it mean to do it manually in another question = it is usually too complex.
The easiest way is your current approach - delete everything and add it again but it is really ugly. Another approach is loading the graph from database and manually merge all changes you get from the request = compare these two graph and update the loaded (attached). Be aware that you must manually call DeleteObject for anything you want to delete from the database. Then just call SaveChanges.
Btw. if you are doing wizard why don't you just store current state in session and save everything once user completes the wizard? You will avoid all these complications.
I would like to know when entities in a certain database table are either created or updated. The application is essentially a CMS, and I need to know when changes are made to the content so that I can reindex them for searches.
I know that the autogenerated LINQ to EF class has overridable methods for when certain fields change, but I need to know when the whole object is created/updated, not just a single field. I tried putting it in OnCreated, only to find that meant OnObjectInitialized and not OnObjectInsertedIntoDBTable xD
I did some searching and came across this link. The "Entity State" section looks like its what I want, but I'm not sure how to use this information. Where do I override those methods?
Or perhaps there is a another/better way?
(I also need to know this for another part of the system, which will send notifications when certain content is changed. I would prefer this code to execute automatically when the insert/update occurs instead of placing it in a controller and hoping hoping I always call that method.)
You need to get ObjectStateEntry(s) from the ObjectStateManager property of the ObjectContect.
var objectStateEntries = this.ObjectStateManager.GetObjectStateEntries();
This entries contain every object state you've pulled down per context and what kind of actions where performed on them.
If you are using EF4 you can override the SaveChanges method to include this functionality. I've used this technique to audit every change that occurs in the database instead of triggers.
Consider an iPhone application that is a catalogue of animals. The application should allow the user to add custom information for each animal -- let's say a rating (on a scale of 1 to 5), as well as some notes they can enter in about the animal. However, the user won't be able to modify the animal data itself. Assume that when the application gets updated, it should be easy for the (static) catalogue part to change, but we'd like the (dynamic) custom user information part to be retained between updates, so the user doesn't lose any of their custom information.
We'd probably want to use Core Data to build this app. Let's also say that we have a previous process already in place to read in animal data to pre-populate the backing (SQLite) store that Core Data uses. We can embed this database file into the application bundle itself, since it doesn't get modified. When a user downloads an update to the application, the new version will include the latest (static) animal catalogue database, so we don't ever have to worry about it being out of date.
But, now the tricky part: how do we store the (dynamic) user custom data in a sound manner?
My first thought is that the (dynamic) database should be stored in the Documents directory for the app, so application updates don't clobber the existing data. Am I correct?
My second thought is that since the (dynamic) user custom data database is not in the same store as the (static) animal catalogue, we can't naively make a relationship between the Rating and the Notes entities (in one database) and the Animal entity (in the other database). In this case, I would imagine one solution would be to have an "animalName" string property in the Rating/Notes entity, and match it up at runtime. Is this the best way to do it, or is there a way to "sync" two different databases in Core Data?
Here's basically how I ended up solving this.
While Amorya's and MHarrison's answers were valid, they had one assumption: that once created, not only the tables but each row in each table would always be the same.
The problem is that my process to pre-populate the "Animals" database, using existing data (that is updated periodically), creates a new database file each time. In other words, I can't rely on creating a relationship between the (static) Animal entity and a (dynamic) Rating entity in Core Data, since that entity may not exist the next time I regenerate the application. Why not? Because I have no control how Core Data is storing that relationship behind the scenes. Since it's an SQLite backing store, it's likely that it's using a table with foreign key relations. But when you regenerate the database, you can't assume anything about what values each row gets for a key. The primary key for Lion may be different the second time around, if I've added a Lemur to the list.
The only way to avoid this problem would require pre-populating the database only once, and then manually updating rows each time there's an update. However, that kind of process isn't really possible in my case.
So, what's the solution? Well, since I can't rely on the foreign key relations that Core Data makes, I have to make up my own. What I do is introduce an intermediate step in my database generation process: instead of taking my raw data (which happens to be UTF-8 text but is actually MS Word files) and creating the SQLite database with Core Data directly, I introduce an intermediary step: I convert the .txt to .xml. Why XML? Well, not because it's a silver bullet, but simply because it's a data format I can parse very easily. So what does this XML file have different? A hash value that I generate for each Animal, using MD5, that I'll assume is unique. What is the hash value for? Well, now I can create two databases: one for the "static" Animal data (for which I have a process already), and one for the "dynamic" Ratings database, which the iPhone app creates and which lives in the application's Documents directory. For each Rating, I create a pseudo-relationship with the Animal by saving the Animal entity's hash value. So every time the user brings up an Animal detail view on the iPhone, I query the "dynamic" database to find if a Rating entity exists that matches the Animal.md5Hash value.
Since I'm saving this intermediate XML data file, the next time there's an update, I can diff it against the last XML file I used to see what's changed. Now, if the name of an animal was changed -- let's say a typo was corrected -- I revert the hash value for that Animal in situ. This means that even if an Animal name is changed, I'll still be able to find a matching Rating, if it exists, in the "dynamic" database.
This solution has another nice side effect: I don't need to handle any migration issues. The "static" Animal database that ships with the app can stay embedded as an app resource. It can change all it wants. The "dynamic" Ratings database may need migration at some point, if I modify its data model to add more entities, but in effect the two data models stay totally independent.
The way I'm doing this is: ship a database of the static stuff as part of your app bundle. On app launch, check if there is a database file in Documents. If not, copy the one from the app bundle to Documents. Then open the database from Documents: this is the only one you read from and edit.
When an upgrade has happened, the new static content will need to be merged with the user's editable database. Each static item (Animal, in your case) has a field called factoryID, which is a unique identifier. On the first launch after an update, load the database from the app bundle, and iterate through each Animal. For each one, find the appropriate record in the working database, and update any fields as necessary.
There may be a quicker solution, but since the upgrade process doesn't happen too often then the time taken shouldn't be too problematic.
Storing your SQLite database in the Documents directory (NSDocumentDirectory) is certainly the way to go.
In general, you should avoid application changes that modify or delete SQL tables as much as possible (adding is ok). However, when you absolutely have to make a change in an update, something like what Amorya said would work - open up the old DB, import whatever you need into the new DB, and delete the old one.
Since it sounds like you want a static database with an "Animal" table that can't be modified, then simply replacing this table with upgrades shouldn't be an issue - as long as the ID of the entries doesn't change. The way you should store user data about animals is to create a relation with a foreign key to an animal ID for each entry the user creates. This is what you would need to migrate when an upgrade changes it.
Im using a Core Data model for my iPhone app. I have been looking for a way to instantiate or use an Entity outside the ManagedObjectContext. (This should not be done, I know, Im also more looking for a way to not do that, but get the benefits anyway).
My challenge is that I have a view where the user can search for "Persons", all the search results are parsed and put into a Person managedObject then displayed in a list.
If the user clicks a Person from the list, then and only then would I like the Person entity to be persisted to the store, however this requires me to delete all the other results so they don't get persisted along with the desired one. Also to the best of my knowledge, if the user decides to quite the app, the store is persisted, potentially with all current search results mixed in with real user data!
Is there some way I could have a TempPerson NSObject I could use for the search list? Without, however, me having to manually pull the 45 attributes from the temp object and manually set them on the managedObject!
Sort of like:
NSManagedObject aPersonCorrectlyReturnedFromTheStore = (NSManagedObject *)tempPersonOfJustTypeNSObject
I have seen example code from Apple where they build a temporary store to facilitate undo/redo and other stuff on an object that is not yet persisted. This I feel would be overkill in my situation. I just need to display search results until the user selects a Person to persist.
Hope it is clear what Im trying to do, feeling like my Core Data vocabulary isn't quite large enough yet:)
Thanks for any suggestions.
You could create each temporary person object as an NSDictionary or NSMutableDictionary. You can then create a new Person managed object and use the fact that NSManagedObject instances are KVC compliant and use setValuesForKeysWithDictionary:.
New managed objects that are inserted are not actually persisted until you send the managed object context a save: message.
Keep track of them in a collection (set or array) -- you are probably already doing this since you are presenting the search results somehow. Then, delete (deleteObject:) them all except for the one(s) that the user selects.
The deleted managed objects will never be stored.