Inverse relationship warning - ask for practices on using inverse relationship - iphone

I have the Order table which contains information about a specific order, (when does it start, how many people, etc...) and that Order table has To-Many relationship to Menu Item table. I call that relationship "orderItems".
The compiler gives me warning, "Order.orderedItems -- to-many relationship does not have an inverse: this is an advanced setting (no object can be in multiple destinations for a specific relationship)"
How do you normally do for inverse relationship? Do I need to create a new relationship on the Menu Item to point back to Order, and set the inverse relationship to the "orderedItems"? (In fact, there is no reason for me to save that data in the Menu Item table)
Someone can explain to me why, or at least, point me the reason on why I need to create the inverse relationship on the Menu Item table.
Thanks in advance,

I ran into this warning and problem too! Here's what you need to know...
Lets say you have two tables - Cars and Drivers. You define a one-to-one relationship - one car to one driver, but no inverse.
So now, if you create a car and assign it a driver, if you delete the driver later, and then try and access car.driver, your program will crash. Car.driver will not be nil - it will crash your program.
So, if you don't create an inverse, you can't ever check to see if something is nil, and you can't even safely work with car.driver at all if driver has been deleted.
I don't like how XCode has this set up. It should default to inverse and be a pain in the ass to change it.
Setting up an inverse is simple. Just open your xcdatamodel in XCode, click the existing relationship, and check the inverse box.

Typically you should always add the inverse relationships.
Do not think about Core Data in terms of the tables that will be generated. In fact, Core Data is smart enough not to put the data in more than one table.
Focus on designing a data model that will meet your needs (and add the inverse relationships).

Related

Modeling an inverse relationship in Core Data

Relationships in Core Data just confuse me. I've read and read, but I just don't get it. I guess it doesn't help that I'm usually frustrated when reading. I want to do something really simple:
I have an Entity called Pictures and an Entity called User. I want Users to be able to like and tag other people in pictures, so each Picture entity has two relationships:
Picture Entity:
UsersWhoLikedThePicture (to-many):
Destination: User
Inverse: Picture
UsersWhoAreTaggedInThePicture (to-many):
Destination: User
Inverse: Picture
But this is causing so much mix up in use, that I can't even begin to describe. It's inconsistent. Someone likes a picture causes them to be removed as a tagged user, and like one picture causes your likes from all other pictures to be removed. Ahhh it's such a mess..does my structure look ok? How would I model this?
In addition to Matthias Bauch answer, I could give you some hints to understand relationships.
First, when you deal with Core Data you have to think in term of objects. By means of this astraction you could think at your model as a graph where nodes are the entities that you created in the model, while relationships are the links among those entities.
Now, about relationships, they could be of different types: one-to-one, one-to-many and many-to-many. Based on the type of relationships you have, you can create different link in the objects graph. For example, if a User has a to-many relationship with Picture, it means that each instance of object (of type NSManagedObject) has a link to different Pictures. User works as the source, Pictures, as the destination.
Inverse relationships are used by Core Data to maintain the graph consistency. In particular, they are useful when you deal with delete rules.
Each relationship has a delete rule associated with it. Cascade means that if you delete an object, say the User, Core Data will delete the object (the Pictures) linked to it for you. Deny doesn't allow to delete an User if there are Pictures linked to it. Nullify means the link from a Picture to a User will be broken. It doesn't mean that the objects are deleted. In terms of object graph, it means that you haven't anymore a link between those objects. No Action means that the source is deleted, the destination is always there and it continues to point to an object that doesn't exist anymore. So, unlike Nullify, you need to broke that link manually. If not you could have a graph inconsistency. Try to avoid this type of relationship.
If you want to know something else, let me know.
EDIT
Take a look at Core Data Programming Guide Relationships section for further info.
I don't know if this is even possible, but it sounds like you used the same inverse target for two relationships. Don't do that.
The right way would be something like this:

I don't need/want a key!

I have some views that I want to use EF 4.1 to query. These are specific optimized views that will not have keys to speak of; there will be no deletions, updates, just good ol'e select.
But EF wants a key set on the model. Is there a way to tell EF to move on, there's nothing to worry about?
More Details
The main purpose of this is to query against a set of views that have been optimized by size, query parameters and joins. The underlying tables have their PKs, FKs and so on. It's indexed, statiscized (that a word?) and optimized.
I'd like to have a class like (this is a much smaller and simpler version of what I have...):
public MyObject //this is a view
{
Name{get;set}
Age{get;set;}
TotalPimples{get;set;}
}
and a repository, built off of EF 4.1 CF where I can just
public List<MyObject> GetPimply(int numberOfPimples)
{
return db.MyObjects.Where(d=> d.TotalPimples > numberOfPimples).ToList();
}
I could expose a key, but whats the real purpose of dislaying a 2 or 3 column natural key? That will never be used?
Current Solution
Seeming as their will be no EF CF solution, I have added a complex key to the model and I am exposing it in the model. While this goes "with the grain" on what one expects a "well designed" db model to look like, in this case, IMHO, it added nothing but more logic to the model builder, more bytes over the wire, and extra properties on a class. These will never be used.
There is no way. EF demands unique identification of the record - entity key. That doesn't mean that you must expose any additional column. You can mark all your current properties (or any subset) as a key - that is exactly how EDMX does it when you add database view to the model - it goes through columns and marks all non-nullable and non-computed columns as primary key.
You must be aware of one problem - EF internally uses identity map and entity key is unique identification in this map (each entity key can be associated only with single entity instance). It means that if you are not able to choose unique identification of the record and you load multiple records with the same identification (your defined key) they will all be represented by a single entity instance. Not sure if this can cause you any issues if you don't plan to modify these records.
EF is looking for a unique way to identify records. I am not sure if you can force it to go counter to its nature of desiring something unique about objects.
But, this is an answer to the "show me how to solve my problem the way I want to solve it" question and not actually tackling your core business requirement.
If this is a "I don't want to show the user the key", then don't bind it when you bind the data to your form (web or windows). If this is a "I need to share these items, but don't want to give them the keys" issue, then map or surrogate the objects into an external domain model. Adds a bit of weight to the solution, but allows you to still do the heavy lifting with a drag and drop surface (EF).
The question is what is the business requirement that is pushing you to create a bunch of objects without a unique identifier (key).
One way to do this would be not to use views at all.
Just add the tables to your EF model and let EF create the SQL that you are currently writing by hand.

Core Data and Relationships

I have two objects, a Trip and a Place. A Trip represents a journey from one Place to another Place, ie. a Trip needs a fromPlace and a toPlace. So, this is a 1-to-2 relationship, but I need to know which is the "from" and which is the "to". I am not sure how to model this in Core Data. I have created two entities (Trip, Place), and now I want to setup the relationship(s) so I have a fromPlace and a toPlace. Do I need to add an extra field on the Place entity called isFrom, or similar?
If this was in a database, I would just have a id column on the Place table, and then two columns in the Trip table - fromPlaceId and toPlaceId. How do I achieve something similar in Core Data?
Do I need to add an extra field on the Place entity called isFrom, or similar?
Yes. It's better for you not to think of Core Data as a wrapper around a database; the database intuition sometimes gets in the way.
Don't first think in terms of database and then try to translate it into Core Data. While you're learning how to use Core Data, just think of it as a system of objects which can be saved into a file and persist between two launches of the app.
Then, from the point of view of object-oriented programming, you have a class Trip which has two instance variables fromPlace and toPlace of class Place.
You want to make it persist on a file. So you create an entity Trip which has two relations fromPlace and toPlace, both of which is of entity Place. That's it!
In more detail, fromPlace and toPlace in Trip are both to-one relationships. In Place, you make two to-many relationships, say tripsStartingHere and tripsEndingHere. Then you set tripsStartingHere as the inverse of fromPlace, and tripsEndingHere as the inverse of toPlace.

Core Data not saving a relationship that has been deleted

I have a "to->many" relationship in Core Data with no inverse relationship and the delete rule set to both "Nullify" and "No action" (by that I mean I've tried both with no avail)
Basically I have a MergedStation whose property subStations points to many Station objects: MergedStation.subStation -->> Station
When I call [mergedStation addSubStationsObject:newStation]; (which is dynamically created) everything works great and a new station is added, everything is refreshed and saved correctly so that the next time I open the program, all is right.
However, when I call [mergedStation removeSubStationsObject:stationToRemove]; (also dynamically created), everything deletes correctly only for the current program session. If I close the program and open it again, it's like I didn't change anything. I have the exact same methods called after both the add and remove methods:
[self.managedObjectContext refreshObject:station mergeChanges:YES];
[self.managedObjectContext processPendingChanges];
[self.managedObjectContext saveAndPrintErrors];
I have also tried different combinations of that above code to get it to work. Any help is appreciated!
Joe
It's unclear what you mean by "deletes correctly only for the current program session". The delete rule specifies what should happen when the relationship origin (the MergedStation) is deleted, not what happens when the relationship is broken. Removing the association should not delete any entity instance. If you want to delete the sub station, you must do that manually with -[NSManagedObjectContext deleteObject:].
On a side note, unless you have literally millions of substations (or billions on OS X), you should include the inverse relationship in your managed object model. Core Data is an object graph management framework and object graph management works best (is easiest) when all relationships are bidirectional. Core Data will do a lot of heavy lifting for your if you include the inverse. There's no need for you to make use of the inverse in your code; you can ignore it if you want. The only penalty you pay is a little memory. Until you can prove that extra memory use is detrimental to your code, you should keep the inverse relationship.
I have this same problem. What he means by "deletes correctly only for the current program sessions" is that if you examine the managed object before restarting the application it is in the state you want it to be in (in his case the SubStation object has been removed from the relationship collection).
I added the inverse relationship to the data model as suggested and everything worked magically. Nice to have working, but I would really like to know if there is a way around this without paying the extra memory penalty.
Ugh. I've spent too much time on a problem closely related to this. In my case, I was adding entities to a to-many relationship and losing those changes after relaunching my app. Adding the inverse relationship is what solved the problem, but I don't understand why. I mean, I see why inverse relationships are a good thing, but they should be mandated or at least default by Core Data if a one-way relationship is as useless as it seems to be.
Thanks to Erik P above for giving me the clue I needed! Add those inverse relationships!

How to make "1-to-1" association works correctly in Entity Framework?

I have objects:
type A (Id, Name),
type B (AId, Description).
I want to make relation 1-to-1 (and create it 1-to-[0..1]). All works great exept deleting objects of type A. When I'm trying to delete some object from type A exception occurs.
A relationship is being added or deleted from an AssociationSet ‘...’. With cardinality constraints, a corresponding ‘...’ must also be added or deleted.
Im searched for solution (found editing CSDL for many-to-many), but nothing helps. There is also cascade action defined in the table in Database.
Any suggestions?
UPD: Thanks for answers.
Let's say more clear.
I don't want to implement inheretance betwen A and B
I try to fix problem by editing edmx file (like this http://codepolice.net/2008/12/16/cascade-delete-in-entity-framework/), but no luck. Seems it's only worked for one-to-many.
I just want to have 2 objects with one-to-one relation. For example, Order and OrderDetails. I expected automatic creating/deleting OrderDetails for every Order I have.
1:1 should give an exception if your deleting B right?
I think what you want is 0..1
Right click Add->associations.
under multiplicity:
On the left hand side choose One for A and 0 or 1 on the right for B.
I think you need this if you want an optional description object(B) for A.
You could also move B's fields into A and check not null for those fields right? That might be easier, then I think you could just use A's fields.
Also, I'm not a database designer by a long shot but, wouldn't you want the Data of A in A?
If for instance you had "Person" and his "Home", I would think those would be a good case for 1:1 (or 0..1 real world), because they themselves are 2 distinct objects that other objects can share independently.
Seems like the A_DataObjects just leads to an unnecessary join?
Old Answer below (not looking for inheritance, but leaving for someone else):
OK, I think I ran into this today. I think what you might want to do is define 2 classes as subclasses of a base class (entity). Right click and do add -> inheritance to get started. I didn't get this all working yet, but I think it involves specifying a field in the base, BaseType which can be used to key in on the derived classes.
http://mosesofegypt.net/post/Inheritance-and-Associations-with-Entity-Framework-Part-1.aspx
Note, there's a part 2 and 3 of this.
-David
One way to do this is to have a single entity that maps to 2 tables. See:
http://msdn.microsoft.com/en-us/library/bb896233.aspx
Visual EntityFramework tool doesn't recognize correctly 'on delete cascade' and creates incomplete xml mapping. You have to change edmx file (You can do it with notepad). Instructions here:
http://codepolice.net/2008/12/16/cascade-delete-in-entity-framework/
Worked for me.
Just make a one-to-many relationship, then by creating unique constraints on the foreign keys in the database you can force it to be 1:1. You can find a full tutorial about it here
(This uses Code-First)