Re: Core Data warning: to-many relation does not have an inverse
Re: Core Data warning: to-many relation does not have an inverse
- Subject: Re: Core Data warning: to-many relation does not have an inverse
- From: Ben Trumbull <email@hidden>
- Date: Thu, 11 Dec 2008 02:38:09 -0800
Consultant.interestingEmployees -- to-many relationship does not
have an inverse: this is an advanced setting (no object can be in
multiple destinations for a specific relationship)
This error doesn't make sense to me. Is it trying to say an object
can only "belong" to one to-many relationship?
It says no object can be in multiple destinations for a specific
relationship (no inverse relationship, btw, fully modeled
relationships don't have this issue, because they're fully modeled).
If you have Department 0->> People (no inverse to many to People) with
D1 and D2 being department objects and P1 being a Person, you cannot
have both D1->>{ P1 } and D2->> { P1 } If Department had several no
inverse to-many relationships to People, like goodPeople and
lazyPeople, then P1 could belong once in each across all the
Department objects.
It means you cannot pretend your no inverse to-many is like a many-to-
many relationship. If you try, you'll corrupt your data, and we told
you not to. You explicitly failed to model the part we needed to
verify whether or not your schema is correct, so this was as helpful
as we could be about your future undefined intentions.
If you sit down and think about how one implements many-to-many
relationships in a database, it should be clear why this is the case.
If that didn't make sense, then please, just model the inverse like
the compiler is suggesting.
"this is an advanced setting" == "if you didn't understand this
message, you're going the wrong way. Otherwise, you're probably going
the wrong way"
The other problem is that I do want to define an inverse for
Employee.company, but I can't set it to *both*
Company.currentEmployees and .previousEmployees. I think the answer is
to maintain the Employee.company relationship field myself without any
inverse, but then I get two more warnings for each of the Company to-
many relationships.
Why are you trying to solve that problem ? In the obvious case,
Employee.company needs to be the inverse for currentEmployees because
it's the current company, and the alternatives semantically don't make
any sense. Then, previousEmployees has a separate inverse, if only
for bookkeeping purposes (e.g. so Core Data maintains everything for
you)
If you're trying to model companies and employees having an audit
record all of their previous employments, then you need to do that
explicitly instead of trying to be too clever. I'd recommend a many-
to-many relationship between Employee and Company for "previous
employers" to match "previous employees". You could go with
"lastPreviousEmployer" and make it a one-to-many.
This model was easy to implement in Foundation, I just created a bunch
of Employee objects and just made sure they were in the proper sets
(current/previous/interesting).
... and they were different sets. What you're trying to do is like
having each of the three sets only retain the object once, and promise
on the honor system not to hork everything up when any one of the 4
objects is deallocated. Naturally, Foundation doesn't let you
perpetuate such a sin upon NSSet, although you are free to hurt
yourself like that with CFSet. We split the difference and give you a
compiler warning saying we think it's a pretty bad plan for most
people most of the time.
I also have a Consultant. It has a to-many relationship to
interestingEmployees. Nobody talking to an Employee needs to have
access to the Consultant, so there is no inverse relationship.
It would probably be better to define that kind of logic on the
Objective-C class (NSManagedObject subclass) for the entity. You
could have an inverse to-many in the model, but not expose it as
public API for your class. A lot of people forget that the Objective-
C class doesn't have to resemble the Entity's schema. Data modeling !
= class modeling. There are things you can do much more efficiently
with protocols compared to entity inheritance for example.
If you have lots and lots of these objects (many thousands), then you
might decide the convenience of the automatic book keeping isn't worth
it, and have a no inverse relationship here. However, you will have
to be VERY EXTREMELY careful to fix up these relationships on
Consultant whenever an Employee is deleted. You'll probably also need
to accommodate undo & redo across saves for deleted employees and
other scenarios. Depending on usage patterns and deftness of coding
you may end up wasting more performance cleaning up the Consultants by
hand.
If you don't have many thousands of objects, then just model the
inverse and be done with it. If you have 10^4 or more objects then
the answer is that it depends, but it's usually easier to start with
the inverse and get rid of it later if performance data shows it
problematic.
There's no compelling reason why relationships should necessarily have
an inverse (and there's a build setting to turn off the warning now).
You mainly have to be careful that when you delete or insert an
Employee object, you make sure you adjust the non-inversed
relationships to match.
Pretty much everybody screws up maintaining inverse relationships
eventually, because it only takes the slightest bug, happening just
once, to destroy the object graph persisted in the database and be
corrupted thereafter. You correctly maintain by hand all your no
inverse relationships when multiple threads resolve an merge conflict
while the user is undoing on the main thread, right ? Also, there's
no latency between the time you delete a destination object and the
time you clean up all the no inverse relationships pointing to it,
right ? Asking us to fulfill a fault with the inconsistent state
during any latency would be bad. You've done all that faster than the
framework optimized code so your time was well spent, right ? And
it's surprisingly easy to accidentally violate the next rule when
transferring objects between relationships.
Further, no inverse to-many relationships can only be used as de facto
implicit one-to-many relationships. You cannot use a no inverse to-
many as an implicit many-to-many relationship. If you try, and many
people on cocoadev did, thus inspiring the warning for 10.5, you will
corrupt your database.
There are some unusual circumstances where this feature is extremely
useful, in ways that cannot be easily or efficiently worked around.
That is why we don't just remove it entirely.
But if you don't understand why the API contact specifically says *at
compile time* (not nestled away in documentation you might not have
read, not in a header file, actually enforced by the compiler) that
you've signed the liability waiver, then you probably shouldn't be
using the feature.
- Ben
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden