Nels_P_Olsen wrote:
To achieve all these things, we came up with an infrastructure that generates the code for and compiles "query row" classes when the queries are executed (and also at design time, so the properties can be selected and bound to textboxes or grid columns). These generated classes expose all the selected values in the query as strongly typed properties, provide an IDictionary of the selected entities keyed by their query alias, and also provide metadata on the entities, selected properties, and the query joins. These "query row" classes can be rehydrated from raw ASP postback values, and all rehydrated entities are attached to each other as the HQL query joins define. We also created an "entity data source" in which the developer defines their HQL query, and from which the "query row" objects are exposed.
Why did you choose to dynamically generate the "row class" as you call it instead of doing it statically. I see you state that you do it both at compile time and run-time. Why not have a code generator that runs on the file containing the HQL queries whenever that file is saved? I don't know if that is really elegant or not, but it is interesting that you generate them at compile time to allow the developer to use them easily from the web and then also generate them at run-time. I assume this is to ensure you have the latest query? But wouldn't it be a problem if the query changed since the developer touched the UI code?
Nels_P_Olsen wrote:
Achieving #4 in particular requires careful determination of what entities to insert in what order, to avoid NHibernate exceptions about transient objects being referenced when you try to save/update other ones. The entities cannot be inserted in any random order. To achieve this within the infrastructure so that the developer does not need to do it for the specific web form/page they are working on, it is necessary to parse the JOIN clauses of the HQL and then examine the NHibernate class mappings of the entities involved, to determine which entities must be inserted first. In particular, this solves situations where a single web form (and single HQL query) joins two entities in a many-to-many relationship using an explicit third entity. In this case, the entities being related must be inserted first, then the entity that references each.
But isn't this really what cascade can be used for? We have a simple rule, if persisting a given object should never create a new object we don't set the cascade, if persisting that object is allowed to save the other object we use cascade="save-update". Now we only get the transient exceptions when a developer is "misbehaving" and trying to do something we would never want them to do.
What you did is certainly an interesting approach. I don't think it would work with the approach we took to our objects, but it is interesting none the less.
Thanks for the insight into how some other teams are using the tool.