LLBLGen and hierarchical controls

Posts   
 
    
Posts: 3
Joined: 06-Mar-2007
# Posted on: 06-Mar-2007 09:58:17   

Hi all,

My name is Atanas Korchev and I work for http://www.telerik.com/ - an asp.net control vendor. I would like to ask what are the possibilities to achieve hierarchical databinding to LLBLGen datasources. Right now our hierarchical controls (treeview, menu, panelbar) support the following ways of hierarchical databinding:

  1. DataTable/DataView/DataSet - two properties are a required DataFieldID and DataFieldParentID. They are later used to create a DataRelation object which in turn helps us create the hierarchy of the control (child nodes).
  2. Binding to hierarchical datasource controls introduced in ASP.NET 2.0 - SiteMapDataSource, XmlDataSource or any custom implementation. An example of the latter can be found at

http://msdn2.microsoft.com/en-us/library/system.web.ui.hierarchicaldatasourcecontrol.aspx

Binding to any other types of datasources (IEnumerable in particular) will cause "flat" databinding - no hierarchy will be created.

I am willing to provide techical assistance and I am open to any suggestions you may have - custom implementations of the HierarchicalDataSourceControl decorating existing LLBLGen datasources or some way to add out-of-the-box support in our controls.

I believe the builtin ASP.NET menu and treeview cannot be bound to LLBLGen datasources as well hence I think the first approach (developing a custom HierarchicalDataSourceControl) will be the best way to tackle the problem.

Best Regards, Atanas Korchev

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 06-Mar-2007 12:18:04   

atanas.korchev wrote:

Hi all,

My name is Atanas Korchev and I work for http://www.telerik.com/ - an asp.net control vendor. I would like to ask what are the possibilities to achieve hierarchical databinding to LLBLGen datasources.

Hi Atanas, I'm Frans Bouma and I designed and wrote most of the LLBLGen Pro system. I'll try to address the questions you've asked as best as possible simple_smile

Right now our hierarchical controls (treeview, menu, panelbar) support the following ways of hierarchical databinding: 1. DataTable/DataView/DataSet - two properties are a required DataFieldID and DataFieldParentID. They are later used to create a DataRelation object which in turn helps us create the hierarchy of the control (child nodes). 2. Binding to hierarchical datasource controls introduced in ASP.NET 2.0 - SiteMapDataSource, XmlDataSource or any custom implementation. An example of the latter can be found at

http://msdn2.microsoft.com/en-us/library/system.web.ui.hierarchicaldatasourcecontrol.aspx

Binding to any other types of datasources (IEnumerable in particular) will cause "flat" databinding - no hierarchy will be created.

I am willing to provide techical assistance and I am open to any suggestions you may have - custom implementations of the HierarchicalDataSourceControl decorating existing LLBLGen datasources or some way to add out-of-the-box support in our controls.

I believe the builtin ASP.NET menu and treeview cannot be bound to LLBLGen datasources as well hence I think the first approach (developing a custom HierarchicalDataSourceControl) will be the best way to tackle the problem.

Correct, a new custom hierarchical control has to be written to make this possible. The thing with the hierarchical control is that it's not that well designed so I can't re-use that much code as in: I can't implement the interfaces on the existing datasourcecontrol classes we have because GetHierarchicalView returns a class, not an interface.

I'll explain briefly what our code does in this area. Our collections implement IListSource, which returns an instance of our entityview class (we have two models, selfservicing and adapter, they differ in how persistence is implemented and have different interfaces, selfservicing uses for example IEntityView and adapter uses IEntityView2, the '2' signals a different interface).

The entity view classes we have (one for selfservicing and one for adapter) implement ITypedList, which guarantees the hierarchical traversability of the object graph.

What I've always wondered is why a hierarchical datasource was needed, as traversing the object graph returned from a regular datasourcecontrol already provides the hierarchy (if any) through ITypedList, which can be used to traverse the complete graph: ITypedList reports if the property returns an IList or not which can be used to traverse the next level.

Then, at runtime, you know the structure what to expect at each level, and the data at runtime will give you the nodes to add to the bound control. Example: a collection of customer entities, which contain each 10 order entities in their Orders collection which contain each 5 OrderDetail entities in their OrderDetails collection.

That would result in 3 levels in the tree ('bands') and the data at runtime will fill in the nodes, so the first level will be just the customer nodes, and every customer node will have 10 order nodes, and each order node will have 5 order detail nodes.

If this isn't possible in web scenario's, please correct me, but I think with the current data / code this is already perfectly constructable. simple_smile

To implement IHierarchicalDataSource, I've to do the datasources all over again, and it was already a real joy creating the normal ones wink (hopefully they've updated the docs now) due to the lack of serious documentation but that aside... as I browse deeper and deeper into the hierarchical datasource classes/interfaces, it will require wrapping at a lot of levels.

Would it be possible for you to build support for ITypedList to simply discover hierarchical data or am I missing something which makes ITypedList support useless in webscenarios ?

Frans Bouma | Lead developer LLBLGen Pro
Posts: 3
Joined: 06-Mar-2007
# Posted on: 06-Mar-2007 13:23:38   

Hi Frans,

Thank you for the quick response.

I have another idea - is it possible to make a new datasource control (inheriting from HierarchicalDataSourceControl and using a custom HierarchicalDataSourceView). The view will return an IHierarchicalEnumerable knowing how to traverse the ITypedList exposed by existing LLBLGen datasources. Is this feasible? This could be either a new datasource control (adapter, extender or simply LLBLGenProHierarchicalDataSource wink ) that can be a part of your toolkit or just a code snippet which customers can copy/paste.

If this is not an option we could try embedding the ITypedList traversing logic in our controls. However I would ask you for some code snippet how to implement that. Ideally you could demonstrate how to bind the ASP.NET treeview - we can then adapt the code for our controls.

I would prefer the first approach as it is cleaner. By providing such a datasource control you will automatically support any web control which can bind to hierarchical datasources - be it telerik's, Microsoft's etc.

Regards, Atanas

Jessynoo avatar
Jessynoo
Support Team
Posts: 296
Joined: 19-Aug-2004
# Posted on: 06-Mar-2007 21:02:39   

Hi Frans,

This is my vision, which relates to previous conversations we had.

I think the easiest implementation would be to tap directly in the Entities code base.

You could have both IEntityCore / IEntityCollection(2) implement IHierarchyData, and IEntityCollection(2) would consequently implement IHierarchicalEnumerable.

Let me try to picture that.

I'd add an IEntityRelatedMember interface with: - enum property RelatedMemberType set to Entity or EntityCollection - a "Parent" property exposing an other IEntityRelatedMember - implementation of IHierarchicalData

IEntityCore and IEntityCollection would implement that interface with Parent mapped to _containingEntity for the collection, _parentCollection or an additional _parentEntity for the Entities classes (the "or" means either hard coded or configurable by a parameter - >do we want ...-entity-collection-entity... hierarchical bands or only ...entity-entity...?). Also, in other terms parentEntity would map to containingEntity or parentCollection.containingEntity if applicable.

Of course you may want to keep that kind of temporary structuring variable outside of the entity but I think it's just more convenient, just as for the parentCollection (but you don't seem to use that variable do you?)

I don't think it's much code so far.

Then IHierarchicalData.GetParent would return the Parent property

IHierarchicalData.GetChildren would return a collection of IEntityRelatedMember, - for entities innercollections and related/relatingentities side by side (or alternatively only flatened entities if configurable as suggested above), - the casted entity collection for entitycollections

When when browsing an entity graph hierarchy through GetChildren(), the _parentEntity would be set dynamically by the GetChildren

The other properties of IHierarchicaldata aren't much of a problemn "Path" can be a additional member.

Now coming back to a previous discussion, if you decide implementing the IRelationCollection IEntityCore.Relations() property, you could have a powerful GetRelatedMember(IRelation) returning a IEntityRelatedMember, and I think the whole result would permit getting rid of loads of the Reflection either used inside the runtime libs or advanced user code that leverages it, by providing a mean to navigate the entity graph either instanciated or Not (that's what we miss today) at runtime.

So basically although I agree ITypedList should be sufficient since it exposes all browsable properties, I think its Reflection taste does not really highlight your business model + optimization is tougher, so once again I reckon that exposing a little more the hierarchical structure natively in the base objects would bring some air and ease further improvements.

Cheers

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 07-Mar-2007 09:51:34   

atanas.korchev wrote:

Hi Frans,

Thank you for the quick response.

I have another idea - is it possible to make a new datasource control (inheriting from HierarchicalDataSourceControl and using a custom HierarchicalDataSourceView). The view will return an IHierarchicalEnumerable knowing how to traverse the ITypedList exposed by existing LLBLGen datasources. Is this feasible? This could be either a new datasource control (adapter, extender or simply LLBLGenProHierarchicalDataSource wink ) that can be a part of your toolkit or just a code snippet which customers can copy/paste.

If this is not an option we could try embedding the ITypedList traversing logic in our controls. However I would ask you for some code snippet how to implement that. Ideally you could demonstrate how to bind the ASP.NET treeview - we can then adapt the code for our controls.

I would prefer the first approach as it is cleaner. By providing such a datasource control you will automatically support any web control which can bind to hierarchical datasources - be it telerik's, Microsoft's etc.

I agree that having a full IHierarchicalDataSource implementation is preferable, however there's a problem: Take the graph: customer - Order - OrderDetails. This means that every customer entity in a collection contains its own collectio of order entities which then contain its own collection of order detail entities.

With the hierarchy datasourcecontrol, I have to pull this apart: every node in the control is returned by its own view. However, that's not the same hierarchy. THAT hierarchy has children of the type of the hierarchycontrol's nodes (IHierarchyData), and not of the type of the nodes contained INSIDE the data item in the parent node.

See it as a parallel hierarchy: the entity hierarchy is actually running parallel on the node hierarchy of the hierarchical control.

There's one plus: the hierarchical datasourcecontrol is readonly, so this structure of IHierarchyData objects can be constructed once, traversing the complete graph. However, if something changes in the data, I've no idea what should be happening.

That's the sad part of doing these datasourcecontrols: they're a major part of asp.net 2.0, but what should be done at given time T with action A is unclear. I.o.w.: again trial-error and decompilation of non-extensible code from MS to see what has to be done to get things working. There are all kinds of classes for the hierarchicaldatasource control like the HierarchicalDataSourceIDConverter, do I need to use that? Where?

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 07-Mar-2007 10:00:16   

Jessynoo wrote:

Hi Frans,

This is my vision, which relates to previous conversations we had.

I think the easiest implementation would be to tap directly in the Entities code base.

You could have both IEntityCore / IEntityCollection(2) implement IHierarchyData, and IEntityCollection(2) would consequently implement IHierarchicalEnumerable.

Let me try to picture that.

I'd add an IEntityRelatedMember interface with: - enum property RelatedMemberType set to Entity or EntityCollection - a "Parent" property exposing an other IEntityRelatedMember - implementation of IHierarchicalData

IEntityCore and IEntityCollection would implement that interface with Parent mapped to _containingEntity for the collection, _parentCollection or an additional _parentEntity for the Entities classes (the "or" means either hard coded or configurable by a parameter - >do we want ...-entity-collection-entity... hierarchical bands or only ...entity-entity...?). Also, in other terms parentEntity would map to containingEntity or parentCollection.containingEntity if applicable.

Of course you may want to keep that kind of temporary structuring variable outside of the entity but I think it's just more convenient, just as for the parentCollection (but you don't seem to use that variable do you?)

Yes it's used when you press ESC in an edit action in a winforms grid: EndEdit is then called on the entity object which then has to remove itself from the collection was added to, which is done by consulting parent collection simple_smile

I don't think it's much code so far.

Then IHierarchicalData.GetParent would return the Parent property

IHierarchicalData.GetChildren would return a collection of IEntityRelatedMember, - for entities innercollections and related/relatingentities side by side (or alternatively only flatened entities if configurable as suggested above), - the casted entity collection for entitycollections

When when browsing an entity graph hierarchy through GetChildren(), the _parentEntity would be set dynamically by the GetChildren

The other properties of IHierarchicaldata aren't much of a problemn "Path" can be a additional member.

Now coming back to a previous discussion, if you decide implementing the IRelationCollection IEntityCore.Relations() property, you could have a powerful GetRelatedMember(IRelation) returning a IEntityRelatedMember, and I think the whole result would permit getting rid of loads of the Reflection either used inside the runtime libs or advanced user code that leverages it, by providing a mean to navigate the entity graph either instanciated or Not (that's what we miss today) at runtime.

So basically although I agree ITypedList should be sufficient since it exposes all browsable properties, I think its Reflection taste does not really highlight your business model + optimization is tougher, so once again I reckon that exposing a little more the hierarchical structure natively in the base objects would bring some air and ease further improvements.

Cheers

ITypedList is actually necessary as you can't rely on properties at runtime, because that would probably trigger lazy loading in selfservicing. It's ok though, reflection is very fast in this case, it just traverses some properties and the set is very small.

It looks easy to add the interface on the entities, but there's a catch: cramming 2 parallel hierarchies into one is very very tough, and you often run into problems. In this case, there's another thing which is that there's controller code outside the entity anyway. As the control's data is readonly (the control only supports select), one could build up the structure once, wrap all objects and return the tree. At design time, one could traverse the tree with ITypedList, and at runtime do that too + traverse the tree using that. In selfservicing however there's then a problem with collections which aren't fetched: they're pulled into memory as properties are triggered.

I can cheat with that of course.

Thinking about it again, it's perhaps not a big deal to add this, however I already have a tough time getting everything done on time for v2.1.

As it's a separate controller, it can be added later on, but I don't like that very much.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 3
Joined: 06-Mar-2007
# Posted on: 08-Mar-2007 11:07:12   

Hi

I am glad it won't be a big deal and it will definitely make the community happy. I am willing to help you with anything I could (suggestions, testing some early bits etc). Please, let me know how this fits in your plans for the coming months.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 09-Mar-2007 09:44:27   

atanas.korchev wrote:

Hi

I am glad it won't be a big deal and it will definitely make the community happy. I am willing to help you with anything I could (suggestions, testing some early bits etc). Please, let me know how this fits in your plans for the coming months.

I've added it to the list of features to add but not for the highest priority, as we think this shouldn't hold up a release, though if possible we'll try to add it to v2.1.

Thanks for the help simple_smile I think it won't be before mid-april till there's time for adding features like this.

Frans Bouma | Lead developer LLBLGen Pro
JasonCoder
User
Posts: 4
Joined: 12-Jun-2007
# Posted on: 14-Jun-2007 19:35:54   

How goes the work on the hierarchical datasource control?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Jun-2007 12:17:16   

JasonCoder wrote:

How goes the work on the hierarchical datasource control?

It's been postponed till a later date, as we couldn't get it done on time before v2.5.

Frans Bouma | Lead developer LLBLGen Pro
Jessynoo avatar
Jessynoo
Support Team
Posts: 296
Joined: 19-Aug-2004
# Posted on: 15-Jun-2007 19:32:19   

Hi Frans,

While evaluating the beta (thanks for the hard work simple_smile ), I just read that thread again, and I wish to say I'm glad you introduced the GetAllRelations() and GetRelatedData() methods (the latter would be useful as Public as well), which IMHO seem to move the framework in the right direction.

Now as for moving further and implementing IHierarchicalData, I feel I'm still in line with my suggestions from the post above.

I reckon it was a bit dense and not very well formulated, especially with that double hierarchy thing, which is obviously overkill for an initial implementation, and it might have wasted the main point in the first place.

The main idea is that I think you'd need an interface that both Entity and EntityCollection implement. I named that IEntityRelatedMember above.

Accordingly, the new GetRelatedData() method could return Dictionary<String, IEntityRelatedMember> instead of Dictionary<String, Object>.

That in my opinion is the best way to then implement IHierarchicalData: it's a fact that you have that hierarchal structure composed of entity and entitycollection nodes (quite obvious with XmlSerialization). Now, although I agree Reflection has a role to play, why not having that structure explicitally embedded in the classes. Having that common interface for entities and collections is a key point I think.

Also, concerning your current ITypedList implementation, I think there's something missing for proper hierarchical binding, since related/relating entities are non browsable properties, which implies you can only browse the nodes in a top down manner through inner collections. More generally, I think you could take advantage of a clearer graph structure to relieve the extensive use of reflection when having to explicit that graph.

Then you might need a few tricks to make it work properly, but I don't think any of them is difficult (you can easily cheat with the lazy loading, you can also easily filter out the parent node from the children nodes etc...).

Finally, if you make a move towards it, I would definitally love to see some kind of "IEntityRelatedMember EntityBase2.GetRelatedMember(IRelation)" method.

Cheers

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Jun-2007 21:52:28   

Whoa lot of different things in one post, Jesse simple_smile

Ok, let me bring the bad news first: I can't add it to v2.5, as time is short.

Now that's out of the way, I'm a little lost with what you're suggesting simple_smile . The thing is that the hierarchical datacontrol has a hierarchy of elements it defines, so if you have a node with a customer, the ORDERS of that customer aren't inside that customer entity but in a subnode of the node the customer is in. So it comes down to wrapping related entities in nodes and return these. (I think).

Changing the return type of the GetRelatedData method will be a bit of a pain I think, as existing code works with the dictionary returned at the moment. It's not a big deal though, it's a simple test which I use in the new xml writer stuff as well. This performs well.

Do you want GetRelatedData to be public?

Finally, if you make a move towards it, I would definitally love to see some kind of "IEntityRelatedMember EntityBase2.GetRelatedMember(IRelation)" method.

simple_smile that's possible simple_smile call GetAllRelations(), and every EntityRelation now has inside itself the name of the field mapped onto that relation. So you can use that name to index in the dict returned by GetRelatedData simple_smile Et viola simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Jessynoo avatar
Jessynoo
Support Team
Posts: 296
Joined: 19-Aug-2004
# Posted on: 17-Jun-2007 17:42:03   

I can't add it to v2.5, as time is short.

That's fine, I would not have expected it that quick. I'm rather thinking of the mid-term orientation strategy for your Framework.

The thing is that the hierarchical datacontrol has a hierarchy of elements it defines, so if you have a node with a customer, the ORDERS of that customer aren't inside that customer entity but in a subnode of the node the customer is in. So it comes down to wrapping related entities in nodes and return these. (I think).

I would understand that you don't want your entity graph to embed the hierarchy themself and leave that maintained in external hierarchicaldatasource/hierarchicalview classes, but I would think it's easier to let the entities expose the hierarchy themself than keeping all of that outside in references projections / dictionaries. I mean that's probably already your reason for having the _containingEntity, _containingEntityMappedField or _parentCollection members in the first place. Those members depend on the context in which you build / browse your entities, yet they're useful kept to hand in the entities themselves.

Similarly, the GetParent / GetChildren would depend of the tree root/direction context, but that's easy to set while building the tree. For instance, browsing a IHierarchicalEnumerable could recursively set the Parent flags in the nested entities/collection with null at the root level.

My point then is that IHierarchicalData would need some kind of homogeneous node, and that is tricky considering that an entity graph is made of alterning entities and entity collections unless you group them under a common interface. Accordingly, a potential implementation of IHierarchicalData is described in my initial post above.

Now I mentioned two possible hierarchies but I don't think you need them both at first.

I suggested that the natural hierarchy is:

Customer Node --- |--> Order Collection Node --- | | --> Order 1 Node | | | |--> Order 2 Node | .... | |--> Order n Node | |--> Related Shipping Address Entity Node | |--> Related Country Entity Node ...

whereas you could alternatively thing of an "entity only" hierarchy of that kind:

Customer Node --- | | --> Order 1 Entity Node | |--> Order 2 Entity Node .... |--> Order n Entity Node | |--> Related Shipping Address Entity Node | |--> Related Country Entity Node ...

So I was initially suggesting that with some extra effort, you could support both kinds of hierarchy dynamically, but of course that's overkill and definitely not a requirement at first. I would stick to the first kind of hierarchy first, which IMHO is better implemented by grouping entities and collection into a common kind of node thus that new interface.

the new GetRelatedData() method could return Dictionary<String, IEntityRelatedMember> instead of Dictionary<String, Object>.

It's not a big deal though, it's a simple test which I use in the new xml writer stuff as well. This performs well.

I can see it's just a matter of boxing an Object to an entitybase and if it fails to an entitycollection. But again my point here is that without that common interface, you have nothing more than "Object" to hold either entities or entitycollections. It works, but I think it illustrates that there is some place and some use for introducing a common wrapping concept in there, regardless the IHierarchical issue.

that's possible call GetAllRelations(), and every EntityRelation now has inside itself the name of the field mapped onto that relation. So you can use that name to index in the dict returned by GetRelatedData Et viola

That's a possibility indeed; GetAllRelations() is definitely the key method I have been waiting for. Yet it confirms that a Public access to GetRelatedData is a good idea wink .

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 18-Jun-2007 14:41:10   

I have the feeling we're discussing two completely different things.

Jessynoo wrote:

The thing is that the hierarchical datacontrol has a hierarchy of elements it defines, so if you have a node with a customer, the ORDERS of that customer aren't inside that customer entity but in a subnode of the node the customer is in. So it comes down to wrapping related entities in nodes and return these. (I think).

I would understand that you don't want your entity graph to embed the hierarchy themself and leave that maintained in external hierarchicaldatasource/hierarchicalview classes, but I would think it's easier to let the entities expose the hierarchy themself than keeping all of that outside in references projections / dictionaries.

That's the situation today: they DO expose the graph themselves, however the hierarchy control wants to build another, parallel graph. Parallel graphs aren't mergable, you can only have the second graph point to elements in the first graph, but for navigation purposes, you have to wrap elements of graph 1 (normal entities) in the nodes of graph 2 (hierarchy created by control) to be able to navigate to subnodes in graph 2.

I mean that's probably already your reason for having the _containingEntity, _containingEntityMappedField or _parentCollection members in the first place. Those members depend on the context in which you build / browse your entities, yet they're useful kept to hand in the entities themselves.

Similarly, the GetParent / GetChildren would depend of the tree root/direction context, but that's easy to set while building the tree. For instance, browsing a IHierarchicalEnumerable could recursively set the Parent flags in the nested entities/collection with null at the root level.

My point then is that IHierarchicalData would need some kind of homogeneous node, and that is tricky considering that an entity graph is made of alterning entities and entity collections unless you group them under a common interface. Accordingly, a potential implementation of IHierarchicalData is described in my initial post above.

I don't think an interface is necessary, as the elements are either a collection or an entity. The thing is with a common interface is that it suggests that they're interchangeable types and in some code that could be possible, but it's not what you want, they're not interchangeable types, a collection is a completely different thing than an entity.

So what has to be done is that nodes wrap a single entity and for each related entity a child node of that node is created and for each collection a collection node. These nodes are then a child node of the entity node and THESE nodes then take care of navigating further.

However, a graph isn't necessarily a tree. This thus can go wrong pretty fast.

Now I mentioned two possible hierarchies but I don't think you need them both at first.

I suggested that the natural hierarchy is:

Customer Node --- |--> Order Collection Node --- | | --> Order 1 Node | | | |--> Order 2 Node | .... | |--> Order n Node | |--> Related Shipping Address Entity Node | |--> Related Country Entity Node ...

whereas you could alternatively thing of an "entity only" hierarchy of that kind:

Customer Node --- | | --> Order 1 Entity Node | |--> Order 2 Entity Node .... |--> Order n Entity Node | |--> Related Shipping Address Entity Node | |--> Related Country Entity Node ...

I'd opt for tree 1.

So I was initially suggesting that with some extra effort, you could support both kinds of hierarchy dynamically, but of course that's overkill and definitely not a requirement at first. I would stick to the first kind of hierarchy first, which IMHO is better implemented by grouping entities and collection into a common kind of node thus that new interface.

Though what does that new interface bring? Not that much other than identifying what type it is. To handle it further, there still has to be code in the node for each type. So instead of doing IEntityCore e = element as IEntityCore if(e!=null) { } else { // collection }

one then can do: ElementType t = e.ElementType switch(t) { case ElementType.Entity: ... case ElementType.Collection: }

though does that bring much? I'm not that convinced, because it would suggest the elements are interchangeable over the interface and that's IMHO not the case. There's also no more logic inside the interface implementation so having an object of the type of the interface isn't doing much, one then will cast it anyway to a type which is useful, which wouldn't make much of a difference with the actual situation.

the new GetRelatedData() method could return Dictionary<String, IEntityRelatedMember> instead of Dictionary<String, Object>.

It's not a big deal though, it's a simple test which I use in the new xml writer stuff as well. This performs well.

I can see it's just a matter of boxing an Object to an entitybase and if it fails to an entitycollection. But again my point here is that without that common interface, you have nothing more than "Object" to hold either entities or entitycollections. It works, but I think it illustrates that there is some place and some use for introducing a common wrapping concept in there, regardless the IHierarchical issue.

Though what other method/property than the identifying enum will there be in this interface so 'is' and 'as' aren't helpful.

that's possible call GetAllRelations(), and every EntityRelation now has inside itself the name of the field mapped onto that relation. So you can use that name to index in the dict returned by GetRelatedData Et viola

That's a possibility indeed; GetAllRelations() is definitely the key method I have been waiting for. Yet it confirms that a Public access to GetRelatedData is a good idea wink .

Done simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Jessynoo avatar
Jessynoo
Support Team
Posts: 296
Joined: 19-Aug-2004
# Posted on: 18-Jun-2007 21:09:28   

Hi again,

Well I'm sorry the discussion gets difficult once again. I don't think we're so much apart in the end. Let me try once more and then I can wait with the current 2.5 release till you start working on tackling that very issue.

they DO expose the graph themselves

I think I don't agree completely with that, and that actually quite a few of the long posts I made for a while did intended to have you explicit further the entity graph model. I mean you made good progress IMHO in that direction, yet that thread is about where to move further.

I think we can agree that inner entity / collection properties can't be considered as exposing the graph (any object exposes its structure at design time or through reflection otherwise). So I can assume that an entity graph is exposed by the GetRelatedData() or GetAllRelations(). Then I see several points for improvement:

  • GetRelatedData() returns Dictionary<String,Object>. Well you have to know the boxing trick to actually start browsing the graph
  • Unlike for the Fields members where the generated concrete properties wrap the inner structure from the core holding the IEntityFields values (I'm perfectly fine with that), The GetRelatedData() and GetAllRelations() do wrap the generated concrete relations / entity/ collection members: the graph structure is embedded in the generated code, not in the ORM framework. I think this is mainly what the ORM Framework lacks more, we already discussed that in other terms in the past, and my posts in that thread are definitely about that.
  • GetRelatedData() only brings in concrete new or fetched instances, and you have to do some processing with GetAllRelation if you want to know what to expect regardless effective instanciation

However, a graph isn't necessarily a tree. This thus can go wrong pretty fast

I think again you got me wrong on this.

I know the graph is not necessarely a tree, but IMHO that would be a good start since nearly all of the time there is a direction in the entity set manipulated, or in other words, entity queries do return entity trees (there might be duplicates in the tree, but that's a common problem you solved several times already (XMLSerialization / UltraGrid bands etc...).

Now if you want my feeling, of course I'd like the entities to embed something more general than a tree. I could think of something like:

IEntityCore : IEdgeListGraph<IEntityRelatedMember, IEntityRelation> in the quickgraph syntax.

Of course I probably don't have it right there, but you've got the idea and that is what I would think as an implementation for "exposing the graph"

however the hierarchy control wants to build another, parallel graph. Parallel graphs aren't mergable, you can only have the second graph point to elements in the first graph, but for navigation purposes, you have to wrap elements of graph 1 (normal entities) in the nodes of graph 2 (hierarchy created by control) to be able to navigate to subnodes in graph 2.

Ok let's see what we have there: You can have LLBLGenDataSource implement IHierarchicalDataSource (or another LLBLGenHierarchicalDataSource, but that's another story), and some new LLBLGenProHierarchicalDataSourceView inherit HierarchicalDataSourceView. Then how do you handle the :

public abstract IHierarchicalEnumerable HierarchicalDataSourceView.Select();
  IHierarchyData IHierarchicalEnumerable.GetHierarchyData(object enumeratedItem);

Well you could have EntityView<TEntity> or a new EntityHierarchicalView<TEntity> implement IHierarchicalEnumerable

Then the issue is about what IHierarchyData will be. Right.

Just as a reminder, we need:

    IHierarchicalEnumerable GetChildren();
    IHierarchyData GetParent();

I'd opt for tree 1.

That would mean that you need entity and collection nodes

The thing is with a common interface is that it suggests that they're interchangeable types and in some code that could be possible, but it's not what you want, they're not interchangeable types, a collection is a completely different thing than an entity.

I don't agree with that. Interfaces clearly set the scopes in which their implementions overlap. Entity and EntityCollection already share a few interfaces without confusion, and there are other cases in the ORM framework where <Item> and SomeEnumerable<Item> share a common interface or a common use (Predicate / PredicateExpression ; Expression / ExpressionElement).

Though what other method/property than the identifying enum will there be in this interface so 'is' and 'as' aren't helpful.

Entity and EntityCollection share one thing: You can find either at the end of an entity relation. I don't see anything wrong with putting a name on that, and I do think it helps in implementing the tree 1 scenario. The interface could either implement IHierarchyData or provide a method/structure that serves the object that does so. BTW, "KindOfDataToOutput" is the enum we're talking about, right?

Now the question is does the IHierarchyData need to be embedded inside the entities? Couldn't it be embedded in a special EntityData Node class in charge of everything.

Well the msdn sample http://msdn2.microsoft.com/en-us/library/system.web.ui.hierarchicaldatasourcecontrol.aspx with Folder and File nodes suggests so, and I'm ok with such a solution; I would agree that ideally anything depending on the binding context should be kept outside of the core classes, yet:

  • The core classes already implement interfaces helping with data binding though I'd agree you made it so EntityView handles the rich binding job.
  • There are also existing contextual members such as _parentCollection; accordingly my initial thought was to keep it simple by extending that mechanism
  • By implementing the tree structure outside of the entity core framework, I would be afraid that the external helper keep the job of exposing and dealing with the hierarchy, thus leaving the entity core classes as is, i.e with the lacks mentioned above: if having to deal with a generic entity hierarchy, I and I think other developpers would probably go for the tree binding helper, whereas I think the entities themself should take care of providing hierarchical navigation amongst them, while the external helper should only mirror that structure into the hierarchical binding interfaces
  • I would eventually reckon that clearly sorting the graph issue in the core classes would relieve you from the toughest parts of the Framework's inner bearings. I do think that you spent too much time implementing methods, the complexity of which have something to do with the fact that you navigate the entities "in the dark" (reflection wise etc...)

So to summarize (sorry for the long post), I would agree in the end with the idea that the binding logic can and probably should be kept outside of the entity, as long as the entities provide themself the core logic for navigating the entity graph. The view / binding classes should provide a way to mirror / contextualize an entity graph into a UI, not a convenient way to browse it by wrapping some reflection/structuration effort. Now again I know that you've been doing some progesses towards the same goal (i.e entity navigation becomes easier), and I'm thankful for that, as I can tell you LLBLGen is a critical component for us. I just wanted to bring my contribution in discussing what I still perceive as a core issue for keeping up with the progresses.

Cheers

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 19-Jun-2007 13:34:15   

Jesse, this is my last long reply in this thread. It took me over an hour to write this reply, and I don't want to reply with 1 or two sentences as you spend time to write your reply as well. However, we both have to realize that this discussion isn't about hierarchical controls and adding support for just that, it's about graph examination and graph info gathering and the framework doesn't provide the easy tools to do that today because it doesn't need to have these internally. If these have to be added, it is a feature request and something else than having support for hierarchical controls which have nothing to do with graph examination.

You also have to realize that the framework is in its current form for a lot of reasons and bending that for a reason unknown to the featurelist of the framework is a code smell: it will result in unmaintainable code as code will have a structure without descriptions why it is the way it is.

Jessynoo wrote:

they DO expose the graph themselves

I think I don't agree completely with that, and that actually quite a few of the long posts I made for a while did intended to have you explicit further the entity graph model. I mean you made good progress IMHO in that direction, yet that thread is about where to move further.

Given an object, one can build up the complete graph without having to revert to instances. The logic for this is embedded in the ITypedList implementation of the entity views. Agreed, this is cumbersome to use for OTHER purposes than databinding, but let's not get carried away here: the topic at hand is databinding related so the data structure bound sits there passively till a request comes in for a given piece of data.

I know you want to do more with graphs, outside the databinding scope, which is perfectly fine, but that question asks for different solutions, not necessarily code which is written to fit in a databinding scenario. The reason for that is that databinding code is interested in different things than you might be interested in and for example provides information not available to you when you want to use the graph so you have to investigate more information.

I want to keep these two things separated for the clarity of the discussion as using graphs in-memory in another scenario than databinding asks for different code which has no meaning in the databinding use-case.

I think we can agree that inner entity / collection properties can't be considered as exposing the graph (any object exposes its structure at design time or through reflection otherwise).

I have to dissapoint you, but they do. simple_smile You can visit the complete graph from any entity in that graph if you want to. See: UtilityClasses -> ObjectGraphUtils -> ProduceAdjacencyLists This routine is a public routine which can be used to build up all the info for the graph from the entity passed in. This routine is an essential part of the topology sorter (which is just a depth first search algo as you know) for saves. It doesn't ignore any entity, it builds the complete graph from the references between the entities. You then get back the adjacency lists. Perhaps you miss info from these lists, which might be the case as the lists were build mainly for the sorter of the graph.

If you NEED extra info for the particular purpose you want the graph traversal for, that info has to be added along the way of course and THAT particular routine has to be adjusted and with that perhaps the routines it relies on. However the core can fully traverse the graph as it needs to, there's no limit to that. It doesn't have to rely on reflection to do so.

Another way to determine the full graph in types is the ITypedList implementation in combination with the initial reflection of the properties of the start entity. This is how databinding solves it. This is more work for the user of the graph if he wants to use the logic to traverse the graph for OTHER purposes.

Which is what I think is the case with your situation, am I correct? simple_smile If so, isn't it better to not use this particular tree viewing logic for graph traversal but to focus on a graph traversal routine which does offer you the info needed?

So I can assume that an entity graph is exposed by the GetRelatedData() or GetAllRelations(). Then I see several points for improvement:

  • GetRelatedData() returns Dictionary<String,Object>. Well you have to know the boxing trick to actually start browsing the graph
  • Unlike for the Fields members where the generated concrete properties wrap the inner structure from the core holding the IEntityFields values (I'm perfectly fine with that), The GetRelatedData() and GetAllRelations() do wrap the generated concrete relations / entity/ collection members: the graph structure is embedded in the generated code, not in the ORM framework. I think this is mainly what the ORM Framework lacks more, we already discussed that in other terms in the past, and my posts in that thread are definitely about that.
  • GetRelatedData() only brings in concrete new or fetched instances, and you have to do some processing with GetAllRelation if you want to know what to expect regardless effective instanciation

An interface which doesn't bring anything to the table other than a type distinguishment is IMHO not that useful, as a cast away from that type is required, which has to involve 'as' and a test, which is also the case with 'object'. Dictionary<string, object> doesn't box anything, it uses object as that's the common type between entity and entitycollection. Sure I could add an interface and use that, but that would only be rather cosmetic. The thing is that the object in the value part of the pair has to be casted to the type to use in the code anyway, which has to involve a test, OR the code has to be implemented in the entity and the collection, which is IMHO not what's possible here as we're talking about consumption code into which these objects are passed in.

the graph code isn't in the orm core because llblgen pro doesn't use a central mapping data store in a context/session object.

GetRelatedData and GetAllRelations are methods which are added to implement a given feature. They're not added to have a universal, generic graph extraction methodology for purposes other than what the framework needs. Everything the core needs is already there, so if you need other, more information, that information thus isn't there and has to be added somewhere.

Which falls into the category "Add a feature which accepts an entity and spits out a graph with nodes and in each node as much info about the node as possible". A feature which isn't needed by the framework but COULD BE useful for developers like you. If you want such a feature, then please let's discuss such a feature.

I find it really difficult to discuss all kinds of design decisions at the detail level why I haven't added this or that info to that particular element, because the code added for framework purposes isn't designed to be as generic as possible, but as generic as necessary. Changing details inside framework constructs and routines to accomplish the addition of a new feature is IMHO not the way to go: the feature should be researched first, then the proper changes should be designed and then the changes should be applied to the existing code base.

Just adding/changing framework code for reasons not embedded in any feature provided by the framework makes it impossible to maintain the code base in the end, as there's no ground in the design docs for these detaillistic changes.

However, a graph isn't necessarily a tree. This thus can go wrong pretty fast

I think again you got me wrong on this.

I know the graph is not necessarely a tree, but IMHO that would be a good start since nearly all of the time there is a direction in the entity set manipulated, or in other words, entity queries do return entity trees (there might be duplicates in the tree, but that's a common problem you solved several times already (XMLSerialization / UltraGrid bands etc...).

Now if you want my feeling, of course I'd like the entities to embed something more general than a tree. I could think of something like:

IEntityCore : IEdgeListGraph<IEntityRelatedMember, IEntityRelation> in the quickgraph syntax.

Of course I probably don't have it right there, but you've got the idea and that is what I would think as an implementation for "exposing the graph"

Sure but that has nothing to do with a hierarchical control for asp.net. If you want a feature to expose a graph in full, fine grained details, please say so and we can discuss that and leave this thread as-is, because a full finegrained detailed graph has nothing to do with a hierarchical viewer control as the graph isn't hierarchical. You can SEE it as that, but that doesn't have to mean anything. It's a DAG, not a tree.

however the hierarchy control wants to build another, parallel graph. Parallel graphs aren't mergable, you can only have the second graph point to elements in the first graph, but for navigation purposes, you have to wrap elements of graph 1 (normal entities) in the nodes of graph 2 (hierarchy created by control) to be able to navigate to subnodes in graph 2.

Ok let's see what we have there: You can have LLBLGenDataSource implement IHierarchicalDataSource (or another LLBLGenHierarchicalDataSource, but that's another story),

It's technically impossible to implement both on the same class.

and some new LLBLGenProHierarchicalDataSourceView inherit HierarchicalDataSourceView. Then how do you handle the :

public abstract IHierarchicalEnumerable HierarchicalDataSourceView.Select();
  IHierarchyData IHierarchicalEnumerable.GetHierarchyData(object enumeratedItem);

Well you could have EntityView<TEntity> or a new EntityHierarchicalView<TEntity> implement IHierarchicalEnumerable

Then the issue is about what IHierarchyData will be. Right.

Exactly and there it goes wrong. One could argue that 'Orders' is a 'child' of 'Customer' but is that really true? And what about Employee referenced from Order. That then becomes a grandchild of customer, which is definitely not the case, as customer and employee are referenced from order, employee isn't hierarchically related to customer.

Cramming it into a tree is therefore not that useful. One could try of course, but the end result looks like a tree, but it doesn't make the data be a tree.

Just as a reminder, we need:

    IHierarchicalEnumerable GetChildren();
    IHierarchyData GetParent();

I'd opt for tree 1.

That would mean that you need entity and collection nodes

Yes.

It will look something like this: rootnode -------refers to-----> Customer | | childnode ---------------------->Orders | | +---childnode----------------->+--Order1 | | +---childnode----------------->+--Order2

etc.

So a parallel hierarchy. The hierarchical control is just a viewer of that tree on the left, which is a build up set of nodes from the entities in a graph at the right.

I don't want to merge them into one thing, never. One reason is that it's impossible, the graph at the right isn't a tree (ex: order1.Employee). Another reason is that the entities already are polluted with databinding code which is necessary according to .NET, but which is already a pain: databinding is a typical MVC mechanism, however in .NET, part of the controller is in the model.

With this hierarchical control, one can keep the controller (tree at the left) separated.

The thing is with a common interface is that it suggests that they're interchangeable types and in some code that could be possible, but it's not what you want, they're not interchangeable types, a collection is a completely different thing than an entity.

I don't agree with that. Interfaces clearly set the scopes in which their implementions overlap. Entity and EntityCollection already share a few interfaces without confusion, and there are other cases in the ORM framework where <Item> and SomeEnumerable<Item> share a common interface or a common use (Predicate / PredicateExpression ; Expression / ExpressionElement).

Though what extra does THIS interface bring to the table other than irrelevant sugar? The interface can be used to distinguish elements to process, but that's already the case, the routine GetRelatedData() already gives me proper info, it's a framework method (which was also a reason why it was protected, not public)

Sure, if you want it for purposes OTHER Than the framework features, namely for full graph traversal and examination, THEN you need more info and code which is tailored towards that. It might be that that code utilizes framework code already in place, but nevertheless, it's code which is tailored towards that particular purpose, not for the purpose GetRelatedData is made for, namely access to all related elements of entity E without hassle.

Though what other method/property than the identifying enum will there be in this interface so 'is' and 'as' aren't helpful.

Entity and EntityCollection share one thing: You can find either at the end of an entity relation. I don't see anything wrong with putting a name on that, and I do think it helps in implementing the tree 1 scenario. The interface could either implement IHierarchyData or provide a method/structure that serves the object that does so. BTW, "KindOfDataToOutput" is the enum we're talking about, right?

It can't implement IHierarchyData as the implementing type isn't a tree element per se.

Now the question is does the IHierarchyData need to be embedded inside the entities? Couldn't it be embedded in a special EntityData Node class in charge of everything.

Well the msdn sample http://msdn2.microsoft.com/en-us/library/system.web.ui.hierarchicaldatasourcecontrol.aspx with Folder and File nodes suggests so, and I'm ok with such a solution; I would agree that ideally anything depending on the binding context should be kept outside of the core classes, yet:

You picked a bad example,IMHO. Folders and files ARE a tree. Entities in-memory don't. They're a graph. They COULD be seen as a tree IF the graph fits the aspects of a tree, but that isn't a given and code which assumes that will fail in a lot of occasions.

Now, I agree with you that one could solve the code the framework embeds, by having a rich graph examination routine which delivers a rich graph which can then be examined further.

However the code is setup to decentralize processing of elements. This means that if such a graph routine would be added, the processing would be centralized, the opposite of how it's done now (example: xml production is decentralized inside the objects to serialize, they serialize themselves, not another object).

  • The core classes already implement interfaces helping with data binding though I'd agree you made it so EntityView handles the rich binding job.
  • There are also existing contextual members such as _parentCollection; accordingly my initial thought was to keep it simple by extending that mechanism
  • By implementing the tree structure outside of the entity core framework, I would be afraid that the external helper keep the job of exposing and dealing with the hierarchy, thus leaving the entity core classes as is, i.e with the lacks mentioned above: if having to deal with a generic entity hierarchy, I and I think other developpers would probably go for the tree binding helper, whereas I think the entities themself should take care of providing hierarchical navigation amongst them, while the external helper should only mirror that structure into the hierarchical binding interfaces
  • I would eventually reckon that clearly sorting the graph issue in the core classes would relieve you from the toughest parts of the Framework's inner bearings. I do think that you spent too much time implementing methods, the complexity of which have something to do with the fact that you navigate the entities "in the dark" (reflection wise etc...)

_parentCollection is there for IEditableObject.CancelEdit, as that retarded interface requires an object added with AddNew to remove itself if CancelEdit is called on it. Databinding code is THAT odd. Re-using it for some other purpose is perhaps 'handy' but one has to be careful when that's done, simply because that is a dependency you want to be very clear, and it could cause bugs which are very hard to find.

The entity graphs in-memory are a combination of two graphs actually: 1) the type graph, e.g. a customer entity contains an orders collection. 2) the instance graph, e.g. only order X contains a reference to a present employee entity.

If the design is decentralized, the info about 1) isn't present in a handy in-memory database. LLBLGen Pro is decentralized, for various reasons and that won't change. The downside is that there has to be some code in the framework to gather the info for 1). As that can be inefficient if 2) also has to be determined, these are combined, and this works very well, that is: for the framework. The framework can delegate work to the elements involved and they can do their work without having to refer to a global big structure which contains the info they need to do their job.

Another side effect of this is that if you, the developer on the outside of the framework, want to do graph examination, you lack the code to do that as it isn't there. If that's required, that feature has to be added at some point, however altering tiny little details is not the way to go. I don't mind making GetRelatedData public and altering the templates so they won't break. What I do mind is adding interfaces, moving code around which won't be used inside the framework nor for a feature of the framework.

So to summarize (sorry for the long post), I would agree in the end with the idea that the binding logic can and probably should be kept outside of the entity, as long as the entities provide themself the core logic for navigating the entity graph.

They do provide that. Not in the form you might like, but they do offer that. The thing is, do you or don't you want extensive information for graphs? If so, why are you discussing this in THIS particular thread? simple_smile Graphs != trees and this control can only deal with trees, not graphs.

The view / binding classes should provide a way to mirror / contextualize an entity graph into a UI, not a convenient way to browse it by wrapping some reflection/structuration effort.

If databinding code forces me to implement some bonehead interface like ITypedList, then I'll do so. However it's not my choice to do so. If I had the choice, there wouldn't be an llblgen pro datasource control, no ITypedList implementation etc. because that would all be implemented in some controller or provided by the .net framework or what have you. However because databinding is implemented the way it is, this code is present and added for that purpose.

I don't understand why the current code in say the ITypedList implementation is so much worse than a routine which effectively does the same thing, because there's no central repository with the data, so it has to reflect as well!

Now again I know that you've been doing some progesses towards the same goal (i.e entity navigation becomes easier), and I'm thankful for that, as I can tell you LLBLGen is a critical component for us. I just wanted to bring my contribution in discussing what I still perceive as a core issue for keeping up with the progresses. Cheers

Jesse, if you want graph navigation / examination, just say so. Express what you need in which node and we can discuss how to add that. It would be typically a wrapper around what's already there, but that's not important. What's important is that a NAVIGATOR is a viewer on the graph, not part of the graph itself. So a node in the graph, say a customer instance, can provide info how to move further, but it's the navigator which allows you to move further, as it's not the purpose of the graph node to navigate, it's a node.

Frans Bouma | Lead developer LLBLGen Pro
Jessynoo avatar
Jessynoo
Support Team
Posts: 296
Joined: 19-Aug-2004
# Posted on: 19-Jun-2007 14:58:48   

Thanks for the long reply Frans. I think we spent quite some time in there and we reached the point where the remaining points of disagreement would fit in another thread indeed. I'll leave that for another time. Good discussion though. simple_smile