I've had a few conversations on the Kentico Community Slack that've led to post ideas for when I had a break in work, and it's been a busy time.
I was calling a custom
EmployeeProvider.GetEmployees() DocumentQuery method, and writing code to match up existing records with an expected list and update any fields that need updating. As part of this, I'd paused in the debugger and was flipping through fields, writing down the ones I wanted to update. While doing this, I noticed a "Fields" enumeration, of type "EmployeeFields". Curious, I dug in, and apparently it's based off of
AbstractHierarchicalObject<>, an ancestor class that the Context objects also use, but not one that TreeNodes come from. I'm a little embarrassed that I've never done a thorough read of the generated class data, but it contains all the same field names as the actual Employee object's generated fields. And, the values were the same, at least for primitives. For Attachments, however, instead of the GUID that Employee.EmployeeAttachment actually stores in the database, it was a property returning the DocumentAttachment itself, with the getter as
mInstance.GetFieldDocumentAttachment("EmployeeAttachment"). Looking into the source code, it's querying this.AllAttachments to find the attachment - so it's doing it all within the object itself, rather than doing a separate call... More query-efficient than querying it myself in this case, since it's the only attachment and I needed to look at it for all of my Employee documents anyways.
So that worked well. If you saw my last post, I have been being more careful about adding column specifications to all my queries to save on some query time. In this case, after verifying the Employee Attachment was coming in, I added in
.Columns("Some other fields", "EmployeeAttachment") to the query...1 And suddenly it stopped working! The GUID was still there, but the Fields EmployeeAttachment value wasn't. Aaaaand... this is why I try everything out first and *then* add .Columns() in.
There's a little documentation that happens to describe that there *are* queries that require extra fields, and gives a handy color-coded example... But it's left as an exercise to the reader to figure out what other options require 'extra' fields - and if you're just selecting API properties rather than methods, it can be less obvious.
My general tactic here is to actually decompile the code and see what I can find. In this case, I already mentioned the Fields listing is in my generated Employee cs file, so I was able to start from there. Decompiling the
TreeNode.GetFieldDocumentAttachment method quickly revealed that what it's doing is getting the Guid from the Employee object, and using it to search this.AllAttachments. Diving down into how this.AllAttachments gets populated during the DocumentQuery, I found that it tries to pull it in based on the DocumentID. Document ID wasn't one of the columns I'd pulled in, and so it silently failed, returning null.
Adding in the DocumentID to the Columns specification, immediately fixed it!
I don't really have any good advice for a general scenario here - you really just have to dig into the decompiled code and see what these methods are silently 'requiring', and make sure you're giving it what it needs. I wish the API would check your complex objects for the children they need (so, accessing this.AllAttachments (and by extension, this.GetFieldDocumentAttachment) could return an exception saying that DocumentID is required to make that call. Maybe I'd be cursing it in the other direction if it did, who knows.
That's all for this post.
1: Note, I didn't actually do the naming overrides here, because this was a pre-existing project, it didn't make sense to refactor it all.