It doesn't work because you have imperative code that is run in-memory (not in the db) yet it works on related data (which isn't there) as prefetch paths are only fetched as the outside of a query. Your 3rd query doesn't have an entity query as its outer projection (but a custom dto projection) so it's not going to fetch the prefetch path.
To do this, I think you could use a nested query in the projection: (not tested code)
var q = from c in metaData.Customer
select new CustomerDto()
{
CustomerId = e.CustomerId,
CustomerName = e.CustomerName,
..
Orders = from o in c.Orders
select new OrderDto(o)
}
That of course doesn't add the elements to an existing collection, but creates a new one from teh elements in the nested query.
It's tricky though: the nested query must be related to the outer query.
Example of working query:
LinqMetaData metaData = new LinqMetaData(adapter);
var q = from c in metaData.Customer
where c.CustomerId == "ALFKI"
select new
{
c.CustomerId,
OrderData = c.Orders
.Select(o => new
{
o.OrderId,
OrderDetailData = o.OrderDetails
})
};