Думайте об этом так, потому что это асинхронный момент, когда обратный вызов получен, цикл foreach давно прошел текущий parentThing, и именно поэтому вы получаете смешанные результаты (условия Леманса, я уверен, что кто-то еще сможетдать вам лучший ответ в отношении этого).
В прошлом я видел, что лучше запускать их по очереди и ждать первого результата, прежде чем продолжить, таким образом вы можете сохранить последний запущенный parentThing в глобальной переменной или чем-то подобном, и вы получитеверните правильный дочерний объект.
int counter = 0;
object lastParentThing;
protected void loopParentThings()
{
lastParentThing = ParentThings[counter];
counter++;
Context.Load(Context.GetChildThingForParentQuery(lastParentThing.Id), op =>
{
lastParentThing.Child = op.Entities.FirstOrDefault();
loopParentThings()
},
null);
}