If I construct an object like say, Category, assign the values to the properties, one being the ID that is the same as a row in the database and pass it to the ObejctSet.Attach method and call the SaveChanges on the Context, will it update that row in the database? Or does the entity have to be retrieved first?
Both scenarios are possible. The difference is if you work with attached or detached instance.
You can create object, attach it to context, say to context that object is modified (attaching marks object as unchanged) and save changes.
Or
You can load object from context, modify object, save changes on the same context (you don't need to set it as modified because object context track changes for objects it loaded).
I wrote examples for both scenarios here.
Related
I have a scenario where an Entity has many relationships with other entities. I did some changes in the NSManagedObject of the entity and discarded those changes.
Right now I'm calling managedObjectContext.refresh(entity, mergeChanges: false) and then managedObjectContext.refresh(relatedEntity, mergeChanges: false) on every related entity to ensure having no dangling objects in the context.
What would be the difference if I directly call managedObjectContext.reset()? Should I still need to refresh or mark nil the related entities?
Is there any way to make this code more optimized?
If you call reset, you need to also immediately stop using every managed object you have fetched from that context. All fetches need to be redone after the reset, because it makes the context forget about everything it has already fetched.
There are various patterns for more efficiently creating discardable changes like you describe. One popular choice is:
Create a new managed object context and make it a child context of the current context.
Make your changes in that context.
If you want to save changes, save them. If not, just don't bother. The child context will be deallocated and its changes will be lost.
I need to be able to create new core data entities during runtime. I've written the code to create the objects programmatically, however, I can't add the entities during runtime as the model is immutable.
My problem is similar to this post, however there is no satisfactory answer: How to dyanmic create a new entity (table) via CoreData model?
The documentation regarding changing the core data model explains:
Managed object models are editable until they are used by an object
graph manager (a managed object context or a persistent store
coordinator). This allows you to create or modify them dynamically.
However, once a model is being used, it must not be changed. This is
enforced at runtime—when the object manager first fetches data using a
model, the whole of that model becomes uneditable. Any attempt to
mutate a model or any of its sub-objects after that point causes an
exception to be thrown. If you need to modify a model that is in use,
create a copy, modify the copy, and then discard the objects with the
old model.
However, I'm unclear on what exactly this is saying--that the whole core data model can't be changed once the persistent store coordinator has been used or the attributes/etc of the individual entities can't be changed.
To be clear, I do not want to change the attributes of my current entities, I simply want to add new entities. It just seems weird to me to have to use migration to add new entities.
Any thoughts?
Thanks!
The documentation is pretty clear.
Copy the model.
Apply your changes to the new copy.
Destroy your old MOC, Persistent Store Coordinator, and all objects created from those.
Apply a migration, if necessary.
Create a new Core Data Stack (MOC, PSC, etc) using your updated model.
The migration could be a sticking point, but it should be do-able.
I would like to check with you the best practice to solve the following task in Core Data framework. In my model one of the property for one object type must be unique. Let's say I have object Account - the property name must be unique - it is not allowed to have 2 accounts with same name.
There are 2 possibilities:
either I execute validation before I call insert into context -> at this point my new object is still not inserted into context, so I can call fetch from context and check if there is already account with particular name
or I overwrite built in validation methods and put my validation there as mentioned here:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdValidation.html#//apple_ref/doc/uid/TP40004807-SW1 - all these validation methods are called only after context is saved
I personally like second case, because my validation would be in model. But the problem is that at this point the object is already inserted into context and therefore if I call fetch, the validation always fails, because also the name of new object counts (even if is still not saved permanently). There is a solution for this however. I can check solely the permanent saved objects by creating new temporary managed object context and call fetch
Is this the best practice to execute such a validation. Or am I missing something or there is better way to do it ?
BR
Lubos
I would go about it in the following way. In general, it is advisable to avoid the complexities of multiple contexts, although that too is a pattern demoed by Apple.
Create the new managed object, insert it into the context. Check against existing names. If the name is not valid, prompt for a different name. Repeat until the name is valid. If the user breaks off the process or times out, delete the object.
If you can do that before saving, discarding the object could be as simple as calling [context rollback];.
If you do this in a separate controller, you could do it in a child context. If the user terminates the process, you just throw away the context altogether without saving.
If you find a name exists but suspect that it is the name being created you can easily check it it is the same object (you already have a reference to it). You could also do a fetch (or, more efficiently, countForFetchRequest) with a predicate that excludes this particular object.
NSPredicate(format:"name = %# && (not self = %#)", account.name, account)
I have what I think is a simple task.
I have a method called [self getPerson] that makes a simple GET request from a web service for a Person that returns some JSON and then transforms the JSON into an NSManagedObject. checks for an existing identical Person NSManagedObject, and if none is found, saves the Person into core data. No problem.
However, If I fire off this method twice in a row, I get two Person NSMangedObjects persisted into Core Data. For example:
[self getPerson];
[self getPerson]; ---> yields duplicate `Person` objects saved in core data, no good.
How can I ensure that only one Person object is saved in Core Data (no duplicates allowed)?
I know the issue, I just don't know how to fix it. The issue is that I need a transaction. When [self getPerson] fires the first time, the method checks for an already existing identical Person object, finds none, and saves a new Person into core data. This is correct. When I fire [self getPerson] the second time, the method checks for an already existing Person object, doesn't see one, and is then persisting another Person object. This is not correct. I would like to make it so that the second time, and third time, and fourth time, to the 1000th time, checking for an existing Person object will only occur after the managedObjectContext saveoperation is done. Right now the check for an existing object is happening so fast (before the save is done).
Do I need a serial queue? If so, should this be a dispatch_async or dispatch_sync? I've even toyed with the idea of trying to use a performSelectorWithDelay trick.
Once you create the object it will exist in the database regardless of you calling save. So you should not create a managed object if one exists already. It's not entirely clear what your code logic is but from your description you say you transform the JSON to a managed object and then you check for an identical existing one and if none is found you save. Well when you create the managed object you have created it, so it's too late to check if an identical one exists. Saving does not create the object it just saves it to the store if it hasn't already been saved.
So first check if an person object exists with the attributes in the JSON and if not then create a managed object.
Well, in this case a serial queue will ensure that operations are performed in the correct manner.
From you question, maybe I'm missing something, I cannot understand if the getPerson method is responsible to both get and save data. If not, you should do it.
Anyway, if you use JSON and the person you retrieve form the server has a unique identifier, you should use that to query against Core Data and verify if it exists or not. The correct manner to do it is to implement Implementing Find-or-Create Efficiently.
A simple question. Is the any reason for calling the getPerson twice? Could you not prevent it using a flag (or a transient property)? Just simple ideas.
I am not sure if that makes any sense, but here is an example.
I have a Category object, that my Service hands to the Controller, which uses AutoMapper to create a CategoryViewModel. Hand that off to the view, serve it to the client.
Now when that gets posted back, AutoMapper creates a Category from the Model sent back, and I hand it to the Service that gives it to the Repository to persist to the database.
My question is, what is the correct way of doing this? I assume the object is a detached object when posted back and I need to attach it to the context, mark it dirty and save changes?
Basically two ways of doing the update of the entity:
Attach the entity to the context, mark it as modified using ObjectStateManager.ChangeObjectState Method, call ObjectContext.SaveChanges Method
Load the original entity from DB, apply changes to the original using ObjectContext.ApplyCurrentValues<TEntity> Method, call ObjectContext.SaveChanges Method
Each of those have their own pros and cons. For example the 1st one does not make round trip to get the original entity but fails to address concurrency as well as tries to update every property of the entity, while the 2nd one works best when employing optimistic concurrency, updates only changed properties, but it does make extra trip to Db to get the original entity.
"I assume the object is a detached object when posted back and I need to attach it to the context, mark it dirty and save changes?"
Yes.
Any one of the links on this page should help:
http://www.google.com/search?rlz=1C1CHFX_enUS410US410&sourceid=chrome&ie=UTF-8&q=working+with+dicsonnected+entities+entity+framework