Tue 18 May 2004 06:34:43 AM UTC, original submission:
Now that validation is being called when EOEditingContext does a saveChanges, a lt of things are fixed. But a few others appear to be broken, notably validation on deleteObject:
What's required:
an existing EO with a mandatory to-one relationship
For example, I have Entity1, let's call it 'Person', which has a personId attribute. I also have Entity2, called 'SalaryHistory'. This also has a personId column. Person has an optional to-many, owns destination, propagates primary key, cascades deletes relationship to SalaryHistory. SalaryHistory has a mandary to-one relationsip to Person.
Now. I want to delete an existing instance of SalaryHistory. Let's assume I have it already. My delete code looks like this:
id salaryHistory;
id editingContext;
... get the relevant EO into my EC.
NS_DURING
{
[editingContext deleteObject:salaryHistory];
[editingContext saveChanges];
}
NS_HANDLER
[localException raise];
NS_ENDHANDLER
What happens is this:
We mark the object for deletion. This puts it into _unprocessedDeletes
Then, during _processRecentChanges, we end up removing the object from both sides of its relationship to Person. this sets the personId to nil, using takeValue:forKey: and then any accessor that happens to be set for the attribute.
As a result of this, the object ends up invoking [self willChange], and it gets stuffed into _unprocessedChanges as well.
A little later, we end up calling [EOEditingContext validateChanges]. This happily lets the delete through, but barfs on the spurious change, as the object has no valid key for the obligatory to-one. So we raise an exception.
There appear to be two ways to fix this.
One might be to have removeObject:fromBothSidesOfRelationshipWithKey: to use takeStoredValue:forKey:, but that would blow normal validation where the object is actually being changed, not deleted. Fiddling with this is likely to get complex, and fast.
The approach I have taken is to change [EOEditingContext propagatesDeletesUsingTable:] to this:
- (void) propagatesDeletesUsingTable: (NSHashTable*)deleteTable
changeTable: (NSHashTable*)changeTable
{
NSHashEnumerator enumerator;
id object = nil;
EOFLOGObjectFnStart();
enumerator = NSEnumerateHashTable(deleteTable);
while ((object = (id)NSNextHashEnumeratorItem(&enumerator))) {
[object propagateDeleteWithEditingContext: self];
NSHashRemove(changeTable, object); // Remove the object if it's been added to the change table
}
EOFLOGObjectFnStop();
}
and the call in _processDeletedObjects to this:
[self propagatesDeletesUsingTable: _unprocessedDeletes
changeTable: _unprocessedChanges];
Which seems to solve the problem.
|