开发者

Bug on iOS SDK or my misconception on relationship of inheritance entity?

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜