CLSA Collections Missing Ingredients!?

CLSA Collections Missing Ingredients!?

Old forum URL: forums.lhotka.net/forums/t/408.aspx


jh72i posted on Monday, June 19, 2006

I'm having a problem with my CLSA collections.  This has been mentioned by serveral of you but, as yet, I still have no satisfactory solution so any help would be GREATLY appreciated....

First, to make my explanation a little simple consider the following object model: 'Person' object may have children which would be a

collection of 'Person' objects..

::(People)Group

  -> (Person) Jack
    -:: (People) Jack's Children
      -> (Person) Jack's only child: Jack Junior

  -> (Person) Jill
    -:: (People) Jill's Children
      -> (Person) Jill's 1st Child: Jill Junior
        -:: (People) Jill Junior's Children
          -> (Person) Jill Junior's 1st Child 
      -> (Person) Jill's 2nd Child
      -> (Person) Jill's 3rd Child


Now, what I simply want to do is bind this object model to a grid (I use Infragistics but despite finger point in their direction I don't believe the problem lies with them).  However, when I do bind to a grid the various People collections get sent numerous IBindingList.AddNew() events.  My understanding is that this is CurrencyManager/BindingContext behaviour to find out the schema of an object.  Trouble is that these collection may be 1. read only or 2. have very complex NewObject behaviour that either results in exceptions being raised or performance absolutely dying.

Now, I have read of the IsBindingNow()/IsFinishedBinding() approach but while this works for the first call to SetDataBinding() it does not solve my problem as the tree is navigated and these IBindingList.AddNew() events trigger as + buttons are pressed.

NOW, why am I posting here!?  This is where I'm going to be devils advocate and suggest that the problem must lie in the way collection are implemented in CSLA.  See, when I try this stuff with the DataSet/Relationship approach I do no get any of these issues.  This leads me, obviously, to conclude that the DataView is a lot more clever when it comes to databinding!????  So what does it do that CSLA collection fail to do!?

I have tried implementing the ICustomTypeDescriptor and ITypedList interfaces on the objects/collections but still that damn AddNew() gets called in the background.

I know I'm not alone in dealing with this so please spare a minute to tell me what you've done/are doing to get around this problem.

And, just to note, my object hierarchies/graphs are pretty complex - the People/Person is purely for illustration.

Thanks as always.
jh

Dawn replied on Monday, June 19, 2006

I have the same problem serval months ago. I don't remember the solution exactly.
Can you try not to bind the CSLA collection directly to the Infragistics Grid, using a BindSource Component instead. And set the BindSouce Componet's DataSouce to you CSLA collection. some thing like below.

cardBindingSrouce.DataSouce = Your CSLA collection.

Infragistics Grid.SetDataBinding(cardBindingSouce, "Your CSLA Col Member Name")

it's seem when you object hierarchies/graphs are pretty complex, Grid can't retrieve the type info correctly, especially when the a child collection is empty, this Grid try to create a new child to create it's band which CSLA dones't like, all CSLA BO use factory method to create new one. But the BindSource Component works.

Dawn.



jh72i replied on Monday, June 19, 2006

Hi Dawn, Thanks for that reply.  I'm confused as to what exactly the BindSource Component is? 

ajj3085 replied on Monday, June 19, 2006

Its a new component in .Net 2.0.  The BindingSource component providers a middle layer between the actual datasource and the controls on your forms.  

So you'd typically create the binding source, and (using the designer) set its datasource to a Csla based object.  You then bind the controls to the binding source.  When your form loads, you'd set the BindingSource's DataSource property to an instance of your BO. 

HTH
Andy

jh72i replied on Monday, June 19, 2006

Ah!  Not at 2.0 yet I'm afraid.

RockfordLhotka replied on Monday, June 19, 2006

The key in 1.x or 2.0 is to realize that if you turn on AllowNew in your collection, then you MUST be able to easily and cheaply create a new child object on demand. You mention that the problem doesn't exist with a dataset - which of course it wouldn't, because a datatable is always able to easily and cheaply create a new datarow on demand.

The problem isn't with the way collections are implemented in CSLA as such, but rather with the base assumption of complex data binding (binding with grids and grid-like controls), that when AllowNew returns true that the grid can feel free to create a new row as it sees fit. YOUR object model must conform to this restriction if you set AllowNew to true - that's just a fact of life.

If you can't make your child objects create quickly or cheaply, then you can't set AllowNew to true, and you'll need to have another UI metaphor to allow the user to add new items to your collection.

Dawn replied on Monday, June 19, 2006

I agree with Rocky.
I think the problem is Infragistics Grid, even I set it's AllowAddNew to false, it still call IBingingList.AddNew when you has a complex objet hierarchies/graphs. This dones't occur when the datasouce just a flat CSLA collection. When I use BindingSource Componet, every thing ok. It seem BindingSource Compent found CSLA collection does't support easily AddNew, then the Grid does't complain, and it's also can get all the hierarchies/grahs info. So I think problem is not CSLA collection, it's the way  Infragistics Grid implent to retrieve BO type info.

jh72i replied on Tuesday, June 20, 2006

Again, thanks for the replies.  I read your entry with interest Rocky and my one question was....answered in a way by you Dawn - the fact that my problem is almost always with collections whose AllowNew is set to false.  These ones raise the exception from the Core... which cause all manner of problems when binding to Infragistic controls.

Implementing ICustomType.../etc. theoretically means that the object doesn't actually need to be created by BindingManager/whatever for it to figure out its schema but, of course, this doesn't work consistently.

I'm not at v2.0 yet so am stuck with a very big problem in the UI layer - any suggestions (however whacky) on how I might workaround this problem given that the business layer is complex and, more importantly, complete!? 

Maybe someone would be interested enough to do a Lutz Roeder's .NET Reflector on the new BindingSource component and tell me what it is doing under the hood to circumvent these AddNew problems!?

 

Thanks again.

 

Dawn replied on Tuesday, June 20, 2006

Sorry I can't give more help. below is some comment from Infragistic 6.1 source Code, it's seem at old version, there is a bug. but which version, i don't know, may be you should ask Infragistic for support

// SSP 6/8/05 BR03609
// Added GetItemPropertiesHelper to fix that issue where if a parent binding manager
// doesn't have at least one row in it and the associated IBindingList doesn't allow adding
// rows then the child binding manager can't be created (the BindingContext indexer throws an exception).


jh72i replied on Tuesday, June 20, 2006

Thanks for that Dawn.  Suggestion there is that the problem lies in the BindingContext and not the grid code.  I'll keep trying to find a solution but I'm not optimistic.  Thanks again for your help.

Dawn replied on Tuesday, June 20, 2006

hi, another problem I forget mentioned. Infragistic Grid does't support band order, if you have two child list, then you can not adjust the two child band order programmingly, if the order of them is not important, forget this. If you  care their order in the grid, then one solution which Infragistic Said:

first you datasource must be custom business object, like CSLA,
second you should implement ITypedList your self.

My solution is name the chlid list alphabetically, say AList, BList, yes it is bad name convention, but now the project is overdue, and it's work. I'll try ITypedList later.

jh72i replied on Tuesday, June 20, 2006

Maybe IListSource could solve this particular problem?  Implement IListSource.GetList() and provide some custom sorting!?!

dshafer replied on Thursday, September 13, 2007

Hi all.

It appears that I'm still having this same problem even though I am using a bindingSource in the middle and AllowNew is set to false on both the collection and the bindingSource.  Anybody have a solution to this problem?  I'm starting to think that it's Infragistics related and not so much .Net or CSLA related.

Dustin

jh72i replied on Thursday, January 17, 2008

Folks,
This goes back to an old problem discussed at http://forums.lhotka.net/forums/thread/2054.aspx

What I NEED to figure out is why I can bind my empty collection to a BindingSource and later bind that to a grid and all will work while I get AddNew problems when binding directly to the collection.

i.e. can any of you gurus out there tell me what the BindingSource does that navigates around the problem of AddNew being called to discover the schema of objects in a collection.

See, using BindingSource in the UI of all our apps seems a huge amount of work and I was hoping that I could do something at the base collection classes to sort the problem out (implementing ITypedList, etc. - that kind of thing).

Thanks

 

RockfordLhotka replied on Thursday, January 17, 2008

I doubt the DataSet works any differently. When you bind an empty DataTable to a bindingSource/grid it immediately adds a new row to the DataTable for display in the grid. This occurs through a call to AddNewCore() in the DataTable. You just don't have a lot of heavy initialization code in that AddNewCore() - though you could if you use the partial class support added in .NET 2.0.

I also doubt that it is creating a new item just to discover the schema. In .NET 2.0, using the generic collection base classes to create a collection means that the indexer (Item property in VB) is strongly typed. The normal behavior for data binding is to use reflection against a strongly typed indexer if it exists, and to only fall back to looking at items in the collection as a last resort.

At no point have I seen data binding create a new item in a list to get type information. Instead of doing that, it simply gives up and binds the grid to type Object (meaning no columns).

jh72i replied on Friday, January 18, 2008

Thanks for that reply Rocky - I'm one of your original tormentors and will soon be looking to move our apps to be more inline with your newer versions and approaches - expect more torment :)

It is confusing because the stack trace from binding a collection to a grid is this (from bottom up below) and clearly shows a path to AddNew from a simple data binding operation:

CSLA.Core.BindableBase.dll!CSLA.Core.BindableCollectionBase.System.ComponentModel.IBindingList.AddNew()

System.Windows.Forms.dll!System.Windows.Forms...
.CurrencyManager.AddNew()
.RelatedCurrencyManager.ParentManager_CurrentItemChanged(object sender, System.EventArgs e)
.RelatedCurrencyManager.Bind(System.Windows.Forms.BindingManagerBase parentManager, string dataField)
.BindingContext.EnsureListManager(object dataSource, string dataMember)
.BindingContext.this[object, string].get(object dataSource, string dataMember)

Infragistics.Win.v7.1.dll!Infragistics.Win.DataBindingUtils.GetCurrencyManager(System.Windows.Forms.BindingContext bindingContext, object dataSource, string dataMember)

Infragistics.Win.UltraWinGrid.v7.1.dll!Infragistics.Win.UltraWinGrid...
.UltraGridBase.GetCurrencyManager(string dataMember)
.UltraGridColumn.CreateChildBandBindingManagerHelper(out string newDataMember)
.UltraGridColumn.InitPropertyDescriptor(System.ComponentModel.PropertyDescriptor propertyDescriptor, Infragistics.Win.UltraWinGrid.UltraGridBand[] oldBands = null)
.UltraGridBand.InitColumns(Infragistics.Win.UltraWinGrid.UltraGridBand[] oldBands = null)
.UltraGridBand.InitListManager(System.Windows.Forms.BindingManagerBase bindingManager, string dataMember, Infragistics.Win.UltraWinGrid.UltraGridBand[] oldBands)
.UltraGridLayout.ListManagerUpdated(System.Windows.Forms.BindingManagerBase bindingManager)
.UltraGridLayout.ListManagerUpdated()
.UltraGridBase.Set_ListManager(object newDataSource, string newDataMember)
.UltraGridBase.SetDataBindingHelper(object dataSource, string dataMember, bool hideNewColumns = true, bool hideNewBands = true)
.UltraGridBase.SetDataBinding(object dataSource, string dataMember, bool hideNewColumns, bool hideNewBands)

Just now I'm suspecting that maybe the fact that BusinessCollection is a property of a base UI form that is only populated with an actual business collection at run-time may be a contributing factor. This mechanism allows us to write particularly lightweight UIs by using the patterns of the business objects at base.

BaseUI....
public virtual BusinessCollectionBaseEx BusinessCollection

Example Use....
this.BusinessCollection = Customers.GetCollection(AllowActions.ReadOnly | AllowActions.IsRealtimeSubscriber)
...
this.grdData.SetDataBinding(this.BusinessCollection,"", true, true);

Where Customers Inherit BusinessCollectionBaseEx...
public class Customers: BusinessCollectionBaseEx

Whereas using the BindingSource as a trial I have had to set its type expliticly to, example, typeof(Customers)

mmmm, I'm going to have to investigate that more on Monday but feel free to post any insights you may have.

Have a good weekend all.

 

jh72i replied on Wednesday, February 27, 2008

* from System.Windows.Forms.RelatedCurrencyManager and seems to be the crux of my AddNew problem.
private void ParentManager_CurrentItemChanged(object sender, EventArgs e) { if (!this.ignoreParentCurrentItemChanged) { int listposition = base.listposition; try { base.PullData(); } catch (Exception exception) { base.OnDataError(exception); } if (this.parentManager is CurrencyManager) { CurrencyManager parentManager = (CurrencyManager) this.parentManager; if (parentManager.Count > 0) { this.SetDataSource(this.fieldInfo.GetValue(parentManager.Current)); base.listposition = (this.Count > 0) ? 0 : -1; } else { parentManager.AddNew(); **************** try { this.ignoreParentCurrentItemChanged = true; parentManager.CancelCurrentEdit(); } finally { this.ignoreParentCurrentItemChanged = false; } } } else { this.SetDataSource(this.fieldInfo.GetValue(this.parentManager.Current)); base.listposition = (this.Count > 0) ? 0 : -1; } if (listposition != base.listposition) { this.OnPositionChanged(EventArgs.Empty); } this.OnCurrentChanged(EventArgs.Empty); this.OnCurrentItemChanged(EventArgs.Empty); } }

RockfordLhotka:

I also doubt that it is creating a new item just to discover the schema. In .NET 2.0, using the generic collection base classes to create a collection means that the indexer (Item property in VB) is strongly typed. The normal behavior for data binding is to use reflection against a strongly typed indexer if it exists, and to only fall back to looking at items in the collection as a last resort.

At no point have I seen data binding create a new item in a list to get type information. Instead of doing that, it simply gives up and binds the grid to type Object (meaning no columns).

Copyright (c) Marimer LLC