I face this problem days ago (link ). In will make this short with easy example. I have Company entity which contain to-many Entity employee and to-one Entity CEO (which inherited from employee).
After complete my data model I added 5 employees to this company, the I assign CEO to this company. On testing I called company.employees and expected to get 5 employees, but a result wasn't the same as I expected I also get CEO as one of my employees.
Shocking from this event I begin searching explanation on apple doc, I see two related article Fetching and Entity Inheritance which say
If you define an entity inheritance hierarchy (see “Entity Inheritance”), when you specify a super-entity as the entity for a fetch request, the request returns all matching instances of the super-entity and of sub-entities.
and Relationship Fundamentals saying
A relationship specifies the entity, or the parent entity, of the objects at the destination. This can be the same as the entity at the source (a reflexive relationship). Relationships do not have to be homogeneous. 开发者_开发知识库If the Employee entity has two sub-entities, say Manager and Flunky, then a given department's employees may be made up of Employees (assuming Employee is not an abstract entity), Managers, Flunkies, or any combination thereof.
Is this behavior is normal ? if yes I can come back to redesign my question (here) with a peace of mind. And it would be great if someone could point me to document where this situation is explained in more detail.
Thank you
Updated topic and add repro step Now I'm thinking that this may be some kind of SDK bug. Here is my repro step Make 3 entity
Family
- int generation
- parents as to-may relation to Parent and family as inverse
- child as to-one relation to Child and family as inverse
Parent
- String name
- family to-one relation to Family
Child : Parent
Here is my code
Family *fam;
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Family" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
NSArray *meters = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil];
if ([meters count] > 0) {
NSLog(@"found");
fam = [meters lastObject];
fam.generation = [NSNumber numberWithInt:[fam.generation intValue] + 1];
} else {
NSLog(@"new");
fam = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:self.managedObjectContext];
fam.generation = [NSNumber numberWithInt:1];
[self saveContext];
};
NSLog(@"There are %d paren", [fam.parents count]);
for (Parent *p in fam.parents) {
NSLog(@"name : %@", p.name);
}
Child *child;
if (!fam.child) {
child = [NSEntityDescription insertNewObjectForEntityForName:
[[NSEntityDescription entityForName:@"Child" inManagedObjectContext:self.managedObjectContext] name]
inManagedObjectContext:self.managedObjectContext];
fam.child = child;
}
fam.child.name = [NSString stringWithFormat:@"child number %d", [fam.generation intValue]];
NSLog(@"There are %d parent after adding one child", [fam.parents count]);
Parent *parent = [NSEntityDescription insertNewObjectForEntityForName:
[[NSEntityDescription entityForName:@"Parent" inManagedObjectContext:self.managedObjectContext] name]
inManagedObjectContext:self.managedObjectContext];
parent.name = [NSString stringWithFormat:@"parent number %d", [fam.generation intValue]];
[fam addParentsObject:parent];
NSLog(@"There are %d parent after add parent", [fam.parents count]);
for (Parent *p in fam.parents) {
NSLog(@"name : %@", p.name);
}
[self saveContext];
in short I create family and add one child and one parent to this family and print out some output in the first run I got this result
2011-08-27 19:06:28.271 child[2015:207] new
2011-08-27 19:06:28.276 child[2015:207] There are 0 paren
2011-08-27 19:06:28.278 child[2015:207] There are 0 parent after adding one child
2011-08-27 19:06:28.279 child[2015:207] There are 1 parent after add parent
2011-08-27 19:06:28.280 child[2015:207] name : parent number 1
which is what I expected, then I rerun the app again and this what the weird thing occur
2011-08-27 19:08:12.383 child[2035:207] found
2011-08-27 19:08:12.386 child[2035:207] There are 2 paren
2011-08-27 19:08:12.387 child[2035:207] name : parent number 1
2011-08-27 19:08:12.388 child[2035:207] name : child number 1
2011-08-27 19:08:12.389 child[2035:207] There are 2 parent after adding one child
2011-08-27 19:08:12.390 child[2035:207] There are 3 parent after add parent
2011-08-27 19:08:12.390 child[2035:207] name : parent number 1
2011-08-27 19:08:12.391 child[2035:207] name : parent number 2
2011-08-27 19:08:12.391 child[2035:207] name : child number 2
For no reason child entity is included in fam.parents property.
I don't think the first thing you quoted is your problem. Referring to company.employees is not a fetch request.
My suspicion is that it might have to do with inverse relationships. Do you have a relationship company on Employee, which is the inverse of Company.employees?
Then when you assign the CEO to a company like this:
ceo.company = company;
Then behind the scenes Core Data says, ceo is an Employee. When you assign a company to an Employee, assign the inverse relationship. Assign the Employee to the company's employees relationship. It generates the effect of the following code automatically:
[company addEmployeesObject:ceo];
In my opinion inverse relationships are annoying in Core Data. Lots of times your code does not need them, but they are all but required by the internal workings of Core Data. Let me know if that turns out to be your problem and I might be able to suggest a workaround.
EDIT:
The behavior on the second run of your parent-child example is what I would expect.
You assign
fam.child = child
The inverse relationship of Family.child is Child.family, so Core Data generates the assignment
child.family = fam
Child inherits from Parent. The inverse relationship of Parent.family is Family.parent, so the inverse relationship of Child.family is also Family.parent. Therefore Core Data generates the insertion
[fam addParentsObject:child]
What I didn't expect was the results on the first run. If child is added to parents, it should be added in the managed object context, and should be visible immediately.
You can call it a bug, maybe, but in any case the buggy behavior is actually what you want. What you don't want is what happens on the second run, which is actually the "correct" behavior. Actually, the behavior is really undefined, because you have built an inconsistent data model. Effectively you have said
The inverse of A.b is B.a
The inverse of B.a is A.c
The inverses aren't really inverses. I think you will find a warning about that when you build.
Okay, so how to solve your original problem?
My first suggestion is you don't make CEO inherit from Employee. But if you have your reasons why you want to do that, then here is how to make it work.
Core Data requires you to define inverses and for the inverses to follow Core Data's rules. If you don't want to follow Core Data's rules, then define inverses according to Core Data's rules, but don't use them. So define your data model like this:
Company
employees: (to-many) Employee - inverse isEmployeeOf
ceo: (to-one) CEO - inverse isCEOOf
// unused
people: (to-many) Employee - inverse company
Employee
company: (to-one) Company - inverse people
// unused
isEmployeeOf: (to-one) Company - inverse employees
CEO : Employee
// unused
isCEOOf: (to-one) Company - inverse ceo
Now create a Company myCompany with one Employee mrEmployee and one CEO mrCEO. You might have to set some more things manually since the properties you use don't have inverses defined.
[myCompany addEmployeesObject:mrEmployee];
mrEmployee.company = myCompany; // not automatic, because not official inverse
myCompany.ceo = mrCEO;
mrCEO.company = myCompany;
They will then have the following relationships:
myCompany
employees - {mrEmployee}
ceo - mrCEO
people - {mrEmployee, mrCEO}
mrEmployee
company - myCompany
isEmployeeOf - myCompany
mrCEO
company - myCompany
isEmployeeOf - myCompany
isCEOOf - myCompany
精彩评论