Example LinqBindingList Usage

Example LinqBindingList Usage

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

justin.fisher posted on Monday, March 16, 2009

I am using the 3.6.0 version of the framework. I have a editable root object that contains an editable child list. 

I have a winform with two binding sources:

  1. bsParent.DataSource = _parent;
  2. bsChildList.DataSource = LinqBindingList<ChildObjectType> returned from a Linq query against _parent. 

Filtering, applying changes, and saving of the list are working fine but when I call CancelEdit on the parent object there is no change to the editable child list? 

I have reviewed the Project Tracker sample and read over the Linq to CSLA section of the Expert Business Objects 2008 book but I'm still not clear how to use the LinqBindingList?  Does someone have some example code illustrating the intended usage of the LinqBindingList beyond the simple sorting in ProjectTracker?

RockfordLhotka replied on Monday, March 16, 2009

Did you call BeginEdit() on _parent before doing any binding?

The rules imposed by Windows Forms data binding are very strict, and are explained in some detail in the Using CSLA .NET 3.0 ebook. The CslaActionExtender control discussed in the Expert 2008 Business Objects book helps wrap a lot of the complexity, but the rules are still very strict.

If you expect a CancelEdit() on _parent to undo anything in the child objects, you must unbind all objects before making that call, and you must have called BeginEdit() on _parent before setting up any binding. This is all stuff that CslaActionExtender automates - so it is worth taking a look at it.

justin.fisher replied on Monday, March 16, 2009

I refreshed my memory by reviewing the data binding portion of the CSLA 3.0 eBook.  Apparently I was forgetting a key step in the RebindUI code, I did not reset the child's binding source correctly:

:Broken code
UnbindBindingSource(bsChild, saveObject, false);
UnbindBindingSource(bsParent, saveObject, true);

:Working code
UnbindBindingSource(bsChild, saveObject, false);
UnbindBindingSource(bsParent, saveObject, true);
bsChild.DataSource = bsParent; //added this line

The datagrid is working great when bound directly to the child list.  I am able to apply changes, cancel changes, and save the list. The problems arise when I use Linq To CSLA to filter down bsChild.

I have a tree view control which a user can click to execute the the following code:

var myParent = bsChild.DataSource as Parent;
bsChild.DataSource =
  from items in myParent.ChildList
  where items.Name.Contains(e.Node.Text)
  select items;

This approach seems to work fine for filtering the tree but throws an UndoException when I am trying to run the same cancel code that works fine for the unfiltered list.  I realize that after running the query that bsChild.Datasource is now using a Csla.LinqBindingList<childListItem>, but I was under the impression that this would function just as if I were bound directly to the child?

I realize this is a lengthy post and I appreciated any feedback anyone may have on with this.

JoeFallon1 replied on Tuesday, March 17, 2009

I am not sure if this code:

 from items in myParent.ChildList
  where items.Name.Contains(e.Node.Text)
  select items;

should be changed to this:

 from items in myParent.ChildList
 where items.Name.Contains(e.Node.Text);

I thought someone mentioned that there is no need for the select when a LinqBindingList is involved.

I have no idea about the cancel problem.

I do know that the LBL is a wrapper around the original list. And that the LBL maintains a cross reference of index positions between the original list and the result of your query. If you step into the LBL code you can see that it builds a sorted subset of the list to meet the query criteria and then uses that sorted subset to build the cross reference index and discards the subset itself.

When databinding calls the indexer (Default Item Property in VB) it passes in 0,1,2 etc in sequence. The LBL looks up each index and returns the cross referenced value and then uses that to look up the item in the original list and returns that item to be data bound.

So you can see that it does do the actual work against the "original list". Just in the ordered defined by your query. 


justin.fisher replied on Tuesday, March 17, 2009

I was unable to remove the 'select criteria' portion of my linq query, the compiler seems to require it.  After giving this problem some additional thought, I realized that my question may be more fundamental.  Who should store the LBL result of the Linq query?  Is there anything special (unbinding, call BeginEdit) I need to do before making the assignment? 

When I set things up in code the parent's binding souce is attached to the parent object and the child biding source is attached to the parent with a datamember attribute designating the child list as the attribute it should bind to.  When I am handling the tree view node click I am resetting the childs binding source and I suspect this may be where my problem lies? 

Does anyone have an example illustrating filtering of a child list using Linq that keeps the n-level undo functionality in tact?


justin.fisher replied on Wednesday, March 18, 2009

Apparently the problem I was having had nothing to do with the fact that I was binding to a LinqBindingList.  It was a classic problem with windows forms data binding to a datagridview control.

I used the DataBinding section of the CSLA 3.0 eBook as a guide and implemented the BindUI and RebindUI as was descirbed in that section.

That fixed most of the problems.  I was able to work with the datagrid normally and get the expected behavior when cancelling, applying, and saving the business object.  If I clicked on a specific node (filtered the results which meant bsChild.DataSource was now of type Csla.LinqBindingList<childListItem>) and made changes there then I would get the dreaded UndoException with an edit level mismatch.  I read over this forum post  as it discussed some of the nuances of the databind process and it was very instructive.  

The problem was the result of my changing the child binding
source's data source without removing the bindings first.  The
result of this was that the child objects would always have an edit level mismatch with the parent.

To fix this I simply removed the bindings completely from the child binding source prior to resetting it to the result of a Linq To CSLA query.  This is such an improvement over my old way of filtering, which consisted of a round trip to the database to filter it on the server.  The initial headache of figuring out the process of filtering the data was more than made up for with the performance gains of in memory filtering.

Below is the code in the tree view node click event:

// Disable events on the child's binding source
bsChild.RaiseListChangedEvents = false;

// Retrieve the parent business object from the
// binding source datasource.
var myParent = bsParent.DataSource as Parent;

// Ensure the datasource had a parent object bound to it
if (myParent != null)
  // Unbind the child object from the binding source
  UnbindBindingSource(bsChild, false, false);
  // Reset the child binding source's data source to the
  // result of a Linq to CSLA query.  Changes will map back
  // to the parent object's BusinessListBase property
  bsChild.DataSource =
    from item in myParent.ChildListProperty
    where item.Name.Contains(e.Node.Text)
    select item;

bsChild.RaiseListChangedEvents = true;

Copyright (c) Marimer LLC