I'm developing an application using the EF w/ Self Tracking Entities. Because STE don't support Lazy Loading, what is the preferred method or best practice for development of updating navigation properties of an entity after making changes to the foreign key? This is explained here:
http://msdn.microsoft.com/en-us/library/ff407090.aspx
"When you change the relationship between objects by setting the foreign key property, the reference navigation property is set to null and not synchronized to the appropriate principal entity on the client. After the graph is attached to the object context (for example, after you call the ApplyChanges method), the foreign key properties and navigation properties are synchronized."
This issue has been addressed here:
http://blogs.msdn.com/b/diego/archive/2010/10/06/self-tracking-entities-applychanges-and-duplicate-entities.aspx
But is this really the best way to do this? What are some ways that you guys have used or can think of to get around this limitation? I find it hard to belive anyone is using self tracking entities if its this hard to display navigation properties in you datagrids and have them set to null whenever you make a change to the record. It looks funky in my application when this happens. Sure I can go back to the server after a save and update these properties but I would have a bunch of null navigation properties in my datagird until I do a call to save on the server.
For example, I have a master details scenario in which my user select a record form the datagrid, then edits it in the form below. When changing the foreign key that my combobox is bound to, the record bound to the navigation property in the datagrid gets set to null. How can I avoid this behavior and update the navigation property when the foreign key is changed?
If I try to change the relationship by setting the navigation property instead of the foreign key, i get the following exceptions when I try to submit changes:
"AcceptChanges cannot continue beacuse the objects key values conflict with another object in the ObjectStateManager. Make sure the key values are unique before calling AcceptChanges."
Is it not possible to bind a master details datagrid to navigation properties of entities? Any help or direction on overcoming this would be greatly appreciated.
Thanks!
A little bit frustrating that there seems to be very little information about these types of problems. In the end I was able to figure it out by doing the #2 suggestion from this blog post and grouping my service call to one method:
http://blogs.msdn.com/b/diego/archive/2010/10/06/self-tracking-entities-applychanges-and-duplicate-entities.aspx
The problem tho is that if I want to go back to the database and re load my data I will have to also re-load any lookup tables so they come back in the same graph to avoid the error. I hope this changes in future version of STE.
Related
I am trying to build trival app in osx. I have made entity People with 3 fields: name, age and occupation. App is NOT document based.
ArrayController is binded with App Delegate in parameters section,and in the model key path, I have managedObjectContext
In the attributes inspector,as object controller I have entity name, and People as entity name.
Then I have binded the columns with the array controller, and as controller key I have arranged objects, and under model key path, I have name, age and occupation, (nstableview, has 3 columns).
On the end, I have 2 buttons, add and remove, which are connected with the array controller (add & remove respectively)
When I click on add button, empty record is created, I can edit it, and add the values. Remove button also works fine. But, when I close the app, and open it again, nothing seems to be preserved in core data.
I would like to mention at this point, that I didn't write single line of code so far, I am trying to do everything with binding.
Anyone can tell me what I missed to bind with what? I have searched stackoverflow and google, but I didn't manage to find any solution so far.
Regards, John
In case that anyone encounter the same issue...What I missed to do is connect save action with the table view...
(right click on app delegate and drag from save action to ns table view)
When i did that, problem has been solved, and my data is now saved.
What's the best way to use Core Data if each document on disk corresponds to one Entity instance?
I have a data model file with one entity, and that entity has one attribute of name text and of type Text.
I have a Document.xib that has an NSObjectController that is set to 'Entity' mode and gets the managedObjectContext from the File's Owner. I have an NSTextField that is bound to the Object Controller for the Controller Key 'selection' and the Key Path 'text.' (This is just a test so I can figure out how Core Data works, but my eventual app will also only have one Entity instance per Document)
When I create a new document the textfield says 'No Selection' and is disabled.
I imagine that if I had a Table View or some other kind of way to select from among entity instances the selection would work but I don't nor do I want to. How can I hook up the NSObjectController to only have one Entity instance and to automatically 'select' it?
The intended behaviour is that I type something into the NSTextField, hit Save, close the document, re-open the document and the string in the textfield persists.
This is probably a really basic question but I can't find any tutorials or documents that would address this seemingly simple use case.
Ok, well I haven't figured it all out but my particular issue was being caused by the fact that nothing was being created. I switched out the NSObjectController for an NSArrayController, created an outlet for it in Document.m and added this to windowControllerDidLoadNib:
if (![self.arrayController selectedObjects]) {
[self.arrayController add:#""];
};
Now it seems to just manage the one Entity object.
Core Data model
I have a many-to-many relationship between two of the principal entities; call them Item and Tag. There will be a large number of Documents. Each may have 0 to an arbitrary number of tags.
Each Item entity relevantly has an attribute called name, and a to-many relationship to Tag called tags. Each Tag entity relevantly has an attribute called name, and a to-many relationship to Item called items.
To display them, within the same window, I have: (i) an NSTableView (itemTableView), fed by an NSArrayController (itemArrayController), showing all Items; and (ii) an NSTableView (tagTableView), fed by a different NSArrayController (tagArrayController) showing all Lists.
tagTableView
In the tagTableView, the Table View is bound to tagArrayController, with controllerKey arrangedObjects.
There is only a single table column. The textfield in it is bound to Table Cell View, to model key path objectValue.name. That works so far; it displays all of the lists as expected, and sorts properly when I add a sort descriptor.
Everything has been set up using interface builder in Xcode.
The problem
I have added a checkbox into the tagTableView, in the same table column as the textfield. I am trying to implement two things:
The checkbox should be checked if the user has previously associated the Item with the relevant Tag. If not, the checkbox should be unchecked.
If the user checks an unchecked checkbox, I want to establish a relationship between the two; if the user unchecked a checked checkbox, I want to break that relationship.
The underlying behavior pattern is that the user will not necessarily have control over the tags and may not be able to create them. They are to choose from existing tags, and therefore should be able to see which ones exist, and be able to check/uncheck those that apply.
However, I can't see how to implement this.
Part solutions so far
I can see a possible way to do at least the first task programmatically, roughly along these lines:
Monitor tableViewSelectionDidChange for itemTableView
For a change, update the data source for tagTableView manually, and work out checkbox state by checking them for those Tags which relate to the Item entity that has just been selected, and otherwise unchecking them
However, this looks likely to add complexity, and ideally I would like to do this with bindings if possible.
I have reviewed the Apple Core Data and Bindings references, all the Cocoa books I have, stack overflow and I've also done extensive googling. I have found lots of similar questions (e.g. http://lists.apple.com/archives/cocoa-dev/2011/Mar/msg00164.html) but no answers.
I've also found a way that might work programmatically, but which seems to be like my idea above, at the expense of being able to use bindings (e.g. http://www.raywenderlich.com/14742/core-data-on-ios-5-tutorial-how-to-work-with-relations-and-predicates)
The only relevant question on this site -- Core-Data Check Box Cell with many-to-many data -- is not answered to a level that I can make use of.
It would seem to me that this should be a prime candidate for bindings. I should be able to ask the itemArrayController what Tags (if any) have a relation to its selected item, and then set the checkbox to ticked if it matches the relevant Tag, and unset it if it doesn't. I would expect I should be able to do this within the bindings for the checkbox itself, in interface builder. But I can't work out what model key path or binding to use, or what to set the cocoa bindings for the checkbox to. Am I missing something obvious? Thanks
I'm working on a Hobo app trying to tie together a few models properly.
Activity objects have many Page children. They also have many DataSet children.
Page objects have several different kinds of children. We'll talk about Widget children, but there are several types with the same issue. An instance of a Widget belongs to a Page but also has a belongs_to relationship with a DataSet. Here's the important point: the DataSet must belong to the containing Activity. So for any given #widget:
#widget.page.activity === #widget.data_set.activity
It's easy enough to enforce this constraint in the model with a validation on save. The trick is presenting, within the Widget's form, a select menu of available DataSets which only contains DataSets for the current Activity
I was able to get this working for existing objects using a tag like this:
<data_set-tag: options="&DataSet.activity_is(&this.page.activity)" />
However, for a new Widget, this fails messily, because either &this or &this.page is not yet set. Even for a route which contains the page ID, like /pages/:page_id/widgets/new, I'm not really able to get an Activity to scope the list of DataSets with.
If this was proper Rails, I'd get in to the relevant controller method and make the Activity available to the view as #activity or something of the sort, but in Hobo the controllers seems to be 95% Magicâ„¢ and I don't know where to start. The knowledge of which Activity is current must be in there somewhere; how do I get it out?
This is Hobo 1.3.x on Rails 3.0.x.
ETA: The code producing the errors is in the form tag for Widget, like so:
<extend tag="form" for="Widget">
<old-form merge>
<field-list: fields="&this.field_order">
<data_set-tag: options="&DataSet.activity_is(&this.page.activity)" />
</field-list>
</old-form>
</extend>
As I said above, this works for editing existing Widgets, but not new Widgets; the error is undefined method 'page' for nil:NilClass. Bryan Larsen's answer seems to suggest that &this.page should not be null.
it looks like you tried to post this question to the Hobo Users mailing list -- I got a moderation message, but it doesn't appear that your post got posted, nor can I find it to let it through. Please try reposting it, there are several helpful people on the list that don't monitor the Hobo tag here.
In Hobo 1.3, the new action doesn't support part AJAX, so there really isn't much magic. You can just replace the action with your own:
def new_for_page
#activity = Activity.find(...)
#page = Page.find(params[:page_id])
#widget = #page.widgets.new
end
There is a little bit of magic referenced above: if you're in WidgetsController, assigning to #widget will also assign to this.
But as you said, the knowledge is obviously in there somewhere, and your custom controller action shouldn't be necessary.
This statement seems wrong: However, for a new Widget, this fails messily, because either &this or &this.page is not yet set.
It looks like you're properly using owner actions. /pages/:page_id/widgets/new is the route. In widgets_controller it's the new_for_page action. In a new or new_for action, this is set to an unsaved version of the object. In your action, it should have been created with the equivalent of Page.find(params[:page]).widgets.new. In other words, both this and this.page should be populated.
I'm sure you didn't make your statement up out of thin air, so there's probably something else going on.
In the end, it turned out to be syntax. Instead of
<data_set-tag: options="&DataSet.activity_is(&this.page.activity)" />
I needed
<data_set-tag: options="&DataSet.activity_is(#this.page.activity)" />
(note the #).
We actually made this into a helper method, so the final code is
<data_set-tag: options="&DataSet.activity_is(activity_for(#this))" />
I have a core data 'ShoppingList' which contains 'Item' objects. I store a display order as an attribute of each item.
I would like to update the display order of all other items in the shopping list whenever an item is deleted. The code to do this is working fine when I use it in my view controller (from where the item is deleted), but since it is really related to the business objects and not the view, it would be better placed in either ShoppingList or Item.
Ideally, I would like it incorporated into the deletion of the item. So far I have tried the following:
1) Customize the standard Core Data generated ShoppingList.RemoveItemsObject (making sure to observe KVO before.after). What's strange about this way is that the item passed is stripped of its relationships to other core data entities before it gets to my code, which I need to process display orders correctly.
2) Customize Item.didTurnIntoFault. Same applies - but even attributes of the item are gone by this stage.
One answer would be to simply define a new method on ShoppingList that does my processing and then calls the original removeItemsObject. But I would prefer to know that whenever an item is removed, from anywhere, this is taken care of. This works nicely when I customize awakeFromInsert, for example - I know that whenever an item is created certain things are setup for me. But I'm surprised there's no equivalent for deletion.
Did you try to implement prepareForDeletion? Sounds like it's exactly what you're looking for.
The doc says:
You can implement this method to perform any operations required before the object is deleted, such as custom propagation before relationships are torn down, or reconfiguration of objects using key-value observing.