We created a utility that takes an HQL statement in the first form,
Code:
select mother, offspr, mate.Name
from Eg.DomesticCat as mother
inner join mother.Mate as mate
left outer join mother.Kittens as offspr
and generates the C# code for the equivalent type-safe class. Actually, it generates a class that can be used as a type-safe class with a query of the second form,
Code:
select new Family(mother, mate, offspr)
from Eg.DomesticCat as mother
join mother.Mate as mate
left join mother.Kittens as offspr
but the generated class also contains a "map" of the the selected items and the entities that back them, as well as a version of the query without the SELECT clause. We call these generated classes "entity view row" types. We then have a univeral data access class that recognizes these generated "entity view row" types, executes the modified queries (sans SELECT clause), creates instances of the "entity view row" for each "row" returned by the query, and adds the entities in the "row" to a dictionary (keyed by alias defined in the query) in the "entity view row". A collection of these "entity view row" instances, which we call an "entity view", is returned by the data access class. The "entity view row"'s generated properties simply get and set the underlying property on the associated entity.
This allows us to have type-safe classes that are easily databound to controls (i.e. the query results are "flattened") and at the same time, the underlying entities that back each "entity view row" are available in full from the same result set. Best of all, we don't have to write these type-safe classes -- just type the HQL in the utility, and it spits out the generated code for the type-safe class. We're working on creating an HqlObjectDataSource component, which opens the HQL editor in Visual Studio and adds/updates the generated class in your project.
BTW, the dictionary of backing entity instances is marked as a non-browsable property, so when we databind our result set to controls (e.g. a DataGridView), all of the "selected" properties appear in the control by default, and none of the "overhead" properties.
Also, note that with this approach, every useful selected value in the "original" HQL query will be a primitive scalar property (string, integer, boolean, DateTime, etc). Each "entity view row" returned by the query will automatically have an instance of each entity in the FROM and JOIN clauses, so there's no point in actually selecting entities or entity properties in the "original" HQL specified to our utility.