Persisting relationship with local user object during a Restkit update - ios

I am working on an events app that syncs "Events" with a JSON API using Restkit. The mapping looks roughly like this.
var eventsMapping = RKEntityMapping(forEntityForName: "Event", inManagedObjectStore: managedObjectStore)
eventsMapping.identificationAttributes = ["eventID", "name", "eventDescription"]
eventsMapping.addAttributeMappingsFromDictionary([
"id":"eventID",
"title":"name",
"description":"eventDescription",
(more mapping attributes here, etc...)
])
The events are displayed using a NSFetchedResultsController. Locally, there is a 'User' NSManagedObject that is created...however this object is just local to the app and is not synced to the server via Restkit. The "User" has a one-to-many relationship with Events, with the purpose that a user can "save" events that they are attending. (Note: Right now, both sides of the Core Data relationship delete rules is set to "no action.") The save is roughly done this way.
var managedObjectContext = RKManagedObjectStore.defaultStore().mainQueueManagedObjectContext
currentUser.mutableSetValueForKey("events").addObject(event)
managedObjectContext?.saveToPersistentStore(&error)
So far everything works great, selected events are stored and saved successfully with the user and persists through app relaunches as expected. However, there is one scenario that causes an event to be removed from the user and that is when an update is made to that particular event on the server. When Restkit detects this and updates the event, according to breakpoints I placed in the NSFetchedResultsController didChangeObject, apparently Restkit and/or Core Data is actually deleting the event and then inserting it back with the updates. This is transparent and just fine in most cases, but in this case I'm thinking the initial delete is what is breaking off the event from the user.
Of course, the eventsMapping above doesn't reference any relationship to a user in any way, so that could be another reason why the relationship is broken off. I have been reading more about Restkit relationships and I have used relationship / property mappings in Restkit before successfully to relate objects, but in that scenario both objects existed on the API. In this case, the User here isn't a part of the API at all, only local as I explained. So should I still be using a Restkit relationship mapping? Or perhaps I should be trying to accomplish all of the above via another way?

I figured out the answer, I made a goof in the code above. On the identification attributes for the mapping, I had three different properties in there, when I only should have used the one with the primary key that would never change (eventID).
eventsMapping.identificationAttributes = ["eventID"]
What apparently was happening was because I specified the title/name as an identification attribute, whenever that title changed on the server Restkit would identify it as a new/different object and delete the "old" object and insert the "new" one. When I changed it only to specify the primary key, it triggered an update instead and my relationship with the user persisted.
One more note for others that threw me at first: some older info I came across that helped me solve this said to use a primaryKeyAttribute on the mapping. This is apparently dated information: use the identificationAttributes instead.

Related

using restkit do you have to remap EVERYTHING each time something changes?

Right now I have a server that formats my data exactly how restkit wants it, and restkit just takes it and directly maps it to coredata.
This works fine, but when I start to accumulate a lot of data it becomes slow.
For example, I have one object called "stories" and each story contains an array of "posts". each time a new "post" gets added, I regenerate the "story" object to which the new post belongs to, and return the story object to the user for restkit to map. As a story starts to accumulate many posts, this process becomes very slow for restkit. I would prefer a way to just send back new posts, and then tell restkit "hey, add this post to the array of posts on this story", which is in contrast to what I do now which is more like "replace this story with this one I just returned, which includes all posts including any new or updated ones".
Is this possible within restkit? Am I better served just manipulating core data myself to support updates?
Yes, it's possible.
You can look at 'foreign key mapping' to connect your new posts to the existing story. The most important part is to set the relationship assignment type to Union because the default is replace.

Replicating RKConnectionDescription Without RestKit

I'm in the process of removing RestKit from our iOS app. I'm able to get things that I want into Core Data, but they're not really connected.
For example, we have one network call that returns a list of "Category"s (which have a "categoryID" and a "categoryName"; "Category"s also map to-many "StoreLocation"s). We then have another network call that returns a list of "StoreLocation"s (which, among other things, have a "storeName", "storeID", "storeCategoryIDs"; "StoreLocation"s also map to-many "Category"s).
With RestKit, I could use a RKConnectionDescription to describe that "storeCategoryIDs" drove the relationship to-many "Category"s. With that, if I had a given Category object, I could easily determine which StoreLocations belonged to that category.
I'm struggling to see how to accomplishing this without any RestKit dependencies. I suppose I could, whenever I'm about to insert a new Category or new StoreLocation, fetch all of the opposite managed objects and do this manually, but I seem to be missing some component of Core Data that can do.
The main part you're missing is the predicate applied to the fetch and which uses the identification attributes to find the appropriate existing objects. You do need to run your own fetch as core data will not magically update one object if you create a different new object and insert it.

Coredata relationship breaks once the entity is updated

I am using coredata to save the server data through web services in my application and I am storing relationships as an object to the entity.
I have many entities e.g "Inspirations" and "Products" and both are related to each other. I have a problem whenever the records are updated in the third entity which is "Filters" then the relations of the entities broke and I cannot apply filters on the entities.
[object addRelatedInspirationsObject:related];
This is how I save relationships. I am not able to figure out why the relations are being broken once the entity is updated which has no direct link with the entity.
One thing more if I fetch and save the data of any one of the entities like "Inspirations" then all the relations start to work again.
Your code should work. Here are 2 things you need to check:
Make sure related is not nil when you call your method.
Make sure you call save on a valid managed object context.
From your question it seems that entities have 1 to many relationship between them. And by the code you supplied, every things should work fine. Just make sure, you are using the Filter object from the relationship like object.filter (or obj1.obj2.filter), not accessing it via a direct NSPredicate on Filter entity and updating it. And if you are using FRC, you might also need to generate a fault against the parent entities, to get your UI updates.

Can I prevent RestKit+CoreData from overriding local entity changes?

I've configured a RKObjectMappingProvider subclass with a series of object mappings that map a variety of service endpoints to my local Core Data entities & persistent store. Let's say I have a service endpoint /api/workorders and I use loadObjectsAtResourcePath:usingBlock: to fetch a list of X workorders and persist to Core Data. Next, the user modifies 2 of those entities using the app but doesn't push the changes back to the service.
If the user again calls /api/workorders to fetch the latest workorders, is there a way to not have RestKit automatically override the local modifications to the changed entities?
The short answer is no, you have to do it yourself and it gets nasty quickly.
You would need to override the setter of a custom managed object class.
This kind of thing.
You would need a property to know it has been updated. You will need to create it and set it when the entity gets updated. I would do this at the row level,
and then check the property and only update if the flag is not set.
Of course you then need to handle the case if the user wants to update the value again :)
Restkit is wonderful, but does not handle full syncing for you.

Update relationships when saving changes of EF4 POCO objects

Entity Framework 4, POCO objects and ASP.Net MVC2. I have a many to many relationship, lets say between BlogPost and Tag entities. This means that in my T4 generated POCO BlogPost class I have:
public virtual ICollection<Tag> Tags {
// getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;
I ask for a BlogPost and the related Tags from an instance of the ObjectContext and send it to another layer (View in the MVC application). Later I get back the updated BlogPost with changed properties and changed relationships. For example it had tags "A" "B" and "C", and the new tags are "C" and "D". In my particular example there are no new Tags and the properties of the Tags never change, so the only thing which should be saved is the changed relationships. Now I need to save this in another ObjectContext. (Update: Now I tried to do in the same context instance and also failed.)
The problem: I can't make it save the relationships properly. I tried everything I found:
Controller.UpdateModel and Controller.TryUpdateModel don't work.
Getting the old BlogPost from the context then modifying the collection doesn't work. (with different methods from the next point)
This probably would work, but I hope this is just a workaround, not the solution :(.
Tried Attach/Add/ChangeObjectState functions for BlogPost and/or Tags in every possible combinations. Failed.
This looks like what I need, but it doesn't work (I tried to fix it, but can't for my problem).
Tried ChangeState/Add/Attach/... the relationship objects of the context. Failed.
"Doesn't work" means in most cases that I worked on the given "solution" until it produces no errors and saves at least the properties of BlogPost. What happens with the relationships varies: usually Tags are added again to the Tag table with new PKs and the saved BlogPost references those and not the original ones. Of course the returned Tags have PKs, and before the save/update methods I check the PKs and they are equal to the ones in the database so probably EF thinks that they are new objects and those PKs are the temp ones.
A problem I know about and might make it impossible to find an automated simple solution: When a POCO object's collection is changed, that should happen by the above mentioned virtual collection property, because then the FixupCollection trick will update the reverse references on the other end of the many-to-many relationship. However when a View "returns" an updated BlogPost object, that didn't happen. This means that maybe there is no simple solution to my problem, but that would make me very sad and I would hate the EF4-POCO-MVC triumph :(. Also that would mean that EF can't do this in the MVC environment whichever EF4 object types are used :(. I think the snapshot based change tracking should find out that the changed BlogPost has relationships to Tags with existing PKs.
Btw: I think the same problem happens with one-to-many relations (google and my colleague say so). I will give it a try at home, but even if that works that doesn't help me in my six many-to-many relationships in my app :(.
Let's try it this way:
Attach BlogPost to context. After attaching object to context the state of the object, all related objects and all relations is set to Unchanged.
Use context.ObjectStateManager.ChangeObjectState to set your BlogPost to Modified
Iterate through Tag collection
Use context.ObjectStateManager.ChangeRelationshipState to set state for relation between current Tag and BlogPost.
SaveChanges
Edit:
I guess one of my comments gave you false hope that EF will do the merge for you. I played a lot with this problem and my conclusion says EF will not do this for you. I think you have also found my question on MSDN. In reality there is plenty of such questions on the Internet. The problem is that it is not clearly stated how to deal with this scenario. So lets have a look on the problem:
Problem background
EF needs to track changes on entities so that persistance knows which records have to be updated, inserted or deleted. The problem is that it is ObjectContext responsibility to track changes. ObjectContext is able to track changes only for attached entities. Entities which are created outside the ObjectContext are not tracked at all.
Problem description
Based on above description we can clearly state that EF is more suitable for connected scenarios where entity is always attached to context - typical for WinForm application. Web applications requires disconnected scenario where context is closed after request processing and entity content is passed as HTTP response to the client. Next HTTP request provides modified content of the entity which has to be recreated, attached to new context and persisted. Recreation usually happends outside of the context scope (layered architecture with persistance ignorace).
Solution
So how to deal with such disconnected scenario? When using POCO classes we have 3 ways to deal with change tracking:
Snapshot - requires same context = useless for disconnected scenario
Dynamic tracking proxies - requires same context = useless for disconnected scenario
Manual synchronization.
Manual synchronization on single entity is easy task. You just need to attach entity and call AddObject for inserting, DeleteObject for deleting or set state in ObjectStateManager to Modified for updating. The real pain comes when you have to deal with object graph instead of single entity. This pain is even worse when you have to deal with independent associations (those that don't use Foreign Key property) and many to many relations. In that case you have to manually synchronize each entity in object graph but also each relation in object graph.
Manual synchronization is proposed as solution by MSDN documentation: Attaching and Detaching objects says:
Objects are attached to the object
context in an Unchanged state. If you
need to change the state of an object
or the relationship because you know
that your object was modified in
detached state, use one of the
following methods.
Mentioned methods are ChangeObjectState and ChangeRelationshipState of ObjectStateManager = manual change tracking. Similar proposal is in other MSDN documentation article: Defining and Managing Relationships says:
If you are working with disconnected
objects you must manually manage the
synchronization.
Moreover there is blog post related to EF v1 which criticise exactly this behavior of EF.
Reason for solution
EF has many "helpful" operations and settings like Refresh, Load, ApplyCurrentValues, ApplyOriginalValues, MergeOption etc. But by my investigation all these features work only for single entity and affects only scalar preperties (= not navigation properties and relations). I rather not test this methods with complex types nested in entity.
Other proposed solution
Instead of real Merge functionality EF team provides something called Self Tracking Entities (STE) which don't solve the problem. First of all STE works only if same instance is used for whole processing. In web application it is not the case unless you store instance in view state or session. Due to that I'm very unhappy from using EF and I'm going to check features of NHibernate. First observation says that NHibernate perhaps has such functionality.
Conclusion
I will end up this assumptions with single link to another related question on MSDN forum. Check Zeeshan Hirani's answer. He is author of Entity Framework 4.0 Recipes. If he says that automatic merge of object graphs is not supported, I believe him.
But still there is possibility that I'm completely wrong and some automatic merge functionality exists in EF.
Edit 2:
As you can see this was already added to MS Connect as suggestion in 2007. MS has closed it as something to be done in next version but actually nothing had been done to improve this gap except STE.
I have a solution to the problem that was described above by Ladislav. I have created an extension method for the DbContext which will automatically perform the add/update/delete's based on a diff of the provided graph and persisted graph.
At present using the Entity Framework you will need to perform the updates of the contacts manually, check if each contact is new and add, check if updated and edit, check if removed then delete it from the database. Once you have to do this for a few different aggregates in a large system you start to realize there must be a better, more generic way.
Please take a look and see if it can help http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/
You can go straight to the code here https://github.com/refactorthis/GraphDiff
I know it's late for the OP but since this is a very common issue I posted this in case it serves someone else.
I've been toying around with this issue and I think I got a fairly simple solution,
what I do is:
Save main object (Blogs for example) by setting its state to Modified.
Query the database for the updated object including the collections I need to update.
Query and convert .ToList() the entities I want my collection to include.
Update the main object's collection(s) to the List I got from step 3.
SaveChanges();
In the following example "dataobj" and "_categories" are the parameters received by my controller "dataobj" is my main object, and "_categories" is an IEnumerable containing the IDs of the categories the user selected in the view.
db.Entry(dataobj).State = EntityState.Modified;
db.SaveChanges();
dataobj = db.ServiceTypes.Include(x => x.Categories).Single(x => x.Id == dataobj.Id);
var it = _categories != null ? db.Categories.Where(x => _categories.Contains(x.Id)).ToList() : null;
dataobj.Categories = it;
db.SaveChanges();
It even works for multiple relations
The Entity Framework team is aware that this is a usability issue and plans to address it post-EF6.
From the Entity Framework team:
This is a usability issue that we are aware of and is something we have been thinking about and plan to do more work on post-EF6. I have created this work item to track the issue: http://entityframework.codeplex.com/workitem/864 The work item also contains a link to the user voice item for this--I encourage you to vote for it if you have not done so already.
If this impacts you, vote for the feature at
http://entityframework.codeplex.com/workitem/864
All of the answers were great to explain the problem, but none of them really solved the problem for me.
I found that if I didn't use the relationship in the parent entity but just added and removed the child entities everything worked just fine.
Sorry for the VB but that is what the project I am working in is written in.
The parent entity "Report" has a one to many relationship to "ReportRole" and has the property "ReportRoles". The new roles are passed in by a comma separated string from an Ajax call.
The first line will remove all the child entities, and if I used "report.ReportRoles.Remove(f)" instead of the "db.ReportRoles.Remove(f)" I would get the error.
report.ReportRoles.ToList.ForEach(Function(f) db.ReportRoles.Remove(f))
Dim newRoles = If(String.IsNullOrEmpty(model.RolesString), New String() {}, model.RolesString.Split(","))
newRoles.ToList.ForEach(Function(f) db.ReportRoles.Add(New ReportRole With {.ReportId = report.Id, .AspNetRoleId = f}))

Resources