What is best way to return a subset of children?

What is best way to return a subset of children?

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


snaidich posted on Wednesday, May 06, 2009

Using 3.6, I have blb with children that gets loaded from a database. Each child object has a 'ChildType' field

I want to get a subset of the children object; for instance, all children of type = x. I would like the subset to be the classtype as the original blb object.

I want to initially load the original blb with all the children objects, and then just make subsets as I need them.

so if I have
Cars cars = Cars.GetAllCars(); //Loads from database

I want to do something like
Cars blueCars = cars.GetOnlyBlueCars();

What is the most efficient way to do this?

Can you show a quick code sample?

Thanks,

Steve

JoeFallon1 replied on Thursday, May 07, 2009

Steve,
There should not be a need to have the class type of your subset be the same as the original BLB object. That is an artificial restrition that will cause lots of pain.

What you really need is a list that contains objects of the same same class type as the original list.

CSLA provides 2 solutions to this. One is an older style FilteredBindingList. The other is the new LinqBindingList (LBL).

If you simply write a LINQ query against a BLB you will get back a sorted and/or filtered LBL. (It may state it is IQueryable or something but you can and should cast it to LBL.)

Then you can bind the LBL to a datagrid and as you edit objects in the subset it updates the objects in the original BLB collection! The LBL simply maintains a cross-reference index to the original collection so it always stays in sync.

This should even work with MS Dynamic Query Library. I think the last few bugs (related to multi-column dynmaic sorting) are worked out and should be in the next version. A Std LINQ query should work fine in the current version.

HTH
Joe

snaidich replied on Thursday, May 07, 2009

Joe,

Thanks for your reply. That works great when binding to a datasource. I did not realize that the original blb would get updated by a grid.

However, let's say that I am not binding to a datagrid, and I am using my BLB in some other sort of algorithm. For argument sake, I have created a method as part of my BLB . For instance, lets say I have a method that calculates cars.AverageSpeed(). If the linq query returns an LBL, I will not be able to use that AverageSpeed() method on the subset (blue cars) of my original data (all cars).

How would you suggest that I handle this scenario? Is there any easy way to populate a new BLB based on the LBL?

Thanks again,

Steve


JoeFallon1 replied on Thursday, May 07, 2009

snaidich:
Joe,

Thanks for your reply. That works great when binding to a datasource. I did not realize that the original blb would get updated by a grid.

However, let's say that I am not binding to a datagrid, and I am using my BLB in some other sort of algorithm. For argument sake, I have created a method as part of my BLB . For instance, lets say I have a method that calculates cars.AverageSpeed(). If the linq query returns an LBL, I will not be able to use that AverageSpeed() method on the subset (blue cars) of my original data (all cars).

How would you suggest that I handle this scenario? Is there any easy way to populate a new BLB based on the LBL?

Thanks again,

Steve


If you Enumerate over a LBL then it works correctly to give you the result you want. So a For Each loop over the LBL should allow you to compute Avg. Speed on your subset.

Joe

snaidich replied on Thursday, May 07, 2009

Joe,

Another issue I have encountered:

When I bind the grid (Infragistics WinGrid) directly to the LBL, which is a subset of the original BLB, I can not seem to add new rows to the grid.

When I bind the grid to a binding source whose datasource is set to the original BLB, I can add rows to the grid.

Any ideas how I can allow the LBL to accept new records? I have set the grid to have one column with a default value that matches the query criteria of my linq query.

Thanks,

Steve

JoeFallon1 replied on Thursday, May 07, 2009

snaidich:
Joe,

Another issue I have encountered:

When I bind the grid (Infragistics WinGrid) directly to the LBL, which is a subset of the original BLB, I can not seem to add new rows to the grid.

When I bind the grid to a binding source whose datasource is set to the original BLB, I can add rows to the grid.

Any ideas how I can allow the LBL to accept new records? I have set the grid to have one column with a default value that matches the query criteria of my linq query.

Thanks,

Steve


I have not done winforms databinding so I really do not know. The usual response is to prove it does not work on a std MS grid before playing with a 3rd party grid. Then you can say there is a bug in CSLA and not in the 3rd party grid. But if you do it your way then you don't really know where the bug is.

Maybe someone else has more input here.
Joe

Tom_W replied on Friday, May 08, 2009

Hi Steve

Definitely try with a standard Windows DataGridView first.  If that works then it may well be an Infragistics issue.   (I haven't tried binding a WinGrid to a LBL yet so I can't say whether it should be working I'm afraid).

I've just found some issues with how the WinGrid handles (or in fact doesn't handle) ICancelAddNew calls (there is a separate thread on all of that) and the one lesson I learnt from that is that Infragistics do not intend for the WinGrid to mimic the DataGridView - which means that it may not necessarily work as we'd expect.

Let me know how you get on with the DGV.  We will definitely be looking to get the WinGrid working with an LBL in the near future, so I will try and help with diagnosing the problem if it turns out to be an Infragistics specific issue.

Cheers

Tom

snaidich replied on Friday, May 08, 2009

I tried this with the datagridview and had some problems.

My blb is "cars".

1) TEST 1

If I have a datagridview and I assign datasource:

gridCars.DataSource = carsBindingSource;
carsBindingSource.DataSource = cars;

Then everything works fine. I am able to fetch, modify, add and delete records

2) TEST 2

I am not sure of the correct way to do this but either of these methods DOES correctly load the datagridview

gridCars.DataSource = carsBindingSource;
carsBindingSource.DataSource = cars.Where(x => x.Color == "Blue");

or

gridCars.DataSource = carsBindingSource;
carsBindingSource.DataSource = (Csla.LinqBindingList) from items
in cars
where items.Color.Equals("Blue")
select items;

The DGV loads correctly with either of these two lines. I am able to click on and enter a cell in a new row. HOWEVER, as soon as I type into a cell, the following DataGridViewDataError is thrown:
"Index 1 does not have a value."

Index 1 corresponds to the newly added record.

The carsBindingSource.Adding_New event is correctly raised when I first click on the new row.

Any ideas?

Tom_W replied on Tuesday, May 12, 2009

You've done better than me, I can't get the grid to populate at all!  No doubt this is my crummy code, but for the time being I'm going to have to leave this one alone and get back to working on the core of our app.

mbblum replied on Saturday, May 16, 2009

My expectation is the newly created object has to meet the LINQ Where criteria, in this example Color = "Blue". When the new Car obect is created, are you setting the color to match the criteria, i.e. Color="Blue"? If not, then the action I would expect is that the new Car object does not meet the LINQ criteria, not in the LBL, and won't be available to the datagrid. For a quick test, change the Create code to set the Color = "Blue" and see if that works.

Another thought: Does LINQ auto-magically pick up the new object? It may require a Refresh/Reload to find the new object and added it to the LBL set. Sorry, don't know what LINQ will do behind the scenes in this situation.

ajj3085 replied on Monday, May 18, 2009

That's actually the opposite of what I'd expect, and the opposite of how filtered lists worked prior to linq... the reason being exactly that you have a new (likely invalid object) which you can't edit, because you can't see it.

Csla should be updating the "real" list behind the scenes, as I recall that being one of the features of the CslaLinqList (or whatever it's called), but I can't comment on what's going on here..

RockfordLhotka replied on Monday, May 18, 2009

The LinqBindingList is a view over the original list. If you add an item to the LBL, it should be added to the underlying BLB - it has to be actually, becuase the LBL contains no actual items.

I do believe that a filtered LBL always maintains the filter. So if you add an item to the list that doesn't meet the requirements of the query that created the LBL then the new item will be added to the underlying collection, but won't appear in the LBL (and thus won't appear in your grid).

That is not the behavior of the older FilteredBindingList, but was apparently necessary in LINQ to CSLA due to the way LINQ works. In a FilteredBindingList when you add a new item it remains visible even if it doesn't meet the filter requirements.

ozhug replied on Friday, May 15, 2009

If you do not want to fetch all of the cars from the data source you could always pass a filter to the your dataportal_fetch

Cars cars = Cars.GetwithColour("blue")

and use the passed critria value to limit the returned results

snaidich replied on Saturday, May 16, 2009

I want/need/would like my data_fetch to return ALL records and then be able to get a subset of all the data. The poor man's solution is to make separate calls to the database to get each subset.

I have been able to get the LBL querying method to load a DataGridView. However, I have not been able to get the grid to edit or add a new record to the LBL and update my original objects.

Has anyone done this? Anyone have sample code?

Copyright (c) Marimer LLC