I am performing a core data migration using a mapping model.
The new model adds a relationship on the Report
entity called rootReport
.
I'd like to make this relation mandatory, rather than optional. For existing Report
entities, rootReport
should be a self reference. Reports that will be added in the future will reference other reports as their root, not just themselves.
The link I gave has a good overview of how the expressions in the mapping model work – they're the same things that are compatible with NSExpression. Based on this, I'm using the following mapping rule for rootReport:
FUNCTION($manager,
"destinationInstancesForEntityMappingNamed:sourceInstances:",
"ReportToReport",
$source.SELF)
[I formatted that – in the file it's all squashed up on one line.]
However, I've not managed to get this to work for my rootReport relation. I always get a validation error following migration with the complaint that rootReport is not set for some report:
NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=134110 \"The operation couldn\U2019t be completed. (Cocoa error 134110.)\" UserInfo=0x7b294630 {attribute=rootReport, entity=Report, reason=Validation error missing attribute values on mandatory destination relationship}"
Any clues on what's going wrong, or suggestions on how to debug? The processing is all in App Kit, so by the time the exception shows up I don't have any context to poke and investigate!
There is a good walk through of the hierarchy of mappings, from completely automatic to fully manual, and all the steps between here.
If you want to know more about what you can do with these mapping expressions (which does work well for attributes, at least), there're good articles here and here.
It's easy to set up mapping expressions to call arbitrary code from your project, or existing methods on the data. It's all pretty clean and elegant.
You do this using the FUNCTION
expression, which the default relation mappings call. To call class methods you use FUNCTION(CAST("<Class-Name>", "Class"), "<class-method-name>", <any>, <arguments>, <in>, <a>, <list>)
as shown here.
I'm not sure what was going wrong above.
It's possible that I had made changes to the destination data model after I created the mapping model, which I didn't imagine would be a problem. However, looking at mapping models with a text editor, they contain large binary blobs which I suspect might represent the source and target data models at the time of creation.
I removed and recreated the mapping model and now I'm having no problem with self references of the form:
FUNCTION($manager,
"destinationInstancesForEntityMappingNamed:sourceInstances:",
"ReportToReport",
$source)
This is nearly identical to the expression used in the question, which I found didn't work. Note, this working expression uses simply $source
instead of $source.SELF
. However, I'm pretty sure I would have tried that before, so I don't know if this is the only thing I was getting wrong.