Am I doing this right ?

Am I doing this right ?

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


lukky posted on Thursday, September 03, 2009

Hi,

Just touching base here to see if my design is sound.

I have BB object which exposes a BLB, so let's say Invoices and InvoiceDetails.

The goal is to bind a WinForms DataGridView to the InvoiceDetails property of the Invoice, BUT, the list must be sorted based on a field of InvoiceDetail. I could simply sort the list when fetching from the DB, but the user can move up/down the InvoiceDetails using buttons.

So my design is to expose a new property on the Invoice as follows:

 //child properties
        public LinqBindingList<InvoiceDetail> InvoiceDetails
        {
            get
            {
                return (LinqBindingList<InvoiceDetail>)from o in InvoiceDetails__ orderby o.LineOrder select o;
            }
        }


        public InvoiceDetailsList InvoiceDetails__
        {
            get
            {
                if (!FieldManager.FieldExists(InvoiceDetailsProperty))
                    LoadProperty(InvoiceDetailsProperty, InvoiceDetailsList.NewInvoiceDetailsList());
                return GetProperty(InvoiceDetailsProperty);
            }
        }

So now the "normal" InvoiceDetails is renamed InvoiceDetails__, and the InvoiceDetails property that is displayed in the DataGridView is the LinqBindingList.

This works fine most of the time, except in very race situations when I insert new lines in the Invoice (I copy them from the OrderDetails). Sometimes the copy works, and it seldom fails.

In those cases where it fails, I get an System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.

This gets thrown by "CslaLinqBindingSource`1.get_Item(Int32 Index)".

By breaking  at LinqBindingList.cs line 504 (CSLA version 3.7.0), I was able to see that  the return value of the call to OriginalIndex(index) does return -1. The next line which returns "_list[src]" obviously throws because src == -1.

Is my usage proper ? Or maybe I'm doing something stupid somewhere else (I'm known to do that a lot).

Thank you.


ajj3085 replied on Friday, September 04, 2009

I have this as well, where uses manage the order of invoice line items. Because the use case calls for the details to be ordered by a user, I think that should be built into the BLB itself. So I think in this case you ignore the normal "sorting is just a view over a collection" way of doing things, and build the sorting directly into your InvoiceDetailList.

lukky replied on Saturday, September 05, 2009

ajj3085:
So I think in this case you ignore the normal "sorting is just a view over a collection" way of doing things, and build the sorting directly into your InvoiceDetailList.


Sorry to ask, but how would you do it ? Embarrassed [:$]

ajj3085 replied on Tuesday, September 08, 2009

Well, if you use a List(which Csla does), the items do stay in the order in which they were added (or inserted). Initially I tried to force an order via a property (int Order { get; set; }), but that got messy.. so I threw it out completely. When saving, I have an instance object that the parent passes to each child as it saves. The class is called PositionCounter, and it just has a private field int that's initialized at 0 and a method called GetPosition, which returns that field++. So as line items are inserting or updating themselves they ask this instance for their position.

Now, my solution is much more complicated because my line items are actually heirarchical. So my base LineItem class inherits BusinessBase, but also implements a composite pattern via IEnumerable, so it acts like a collection at the same time... but since I didn't have BLB in the mix, I didn't have to worry about swapping elements causing one to be on the deleted list.

So not sure if that helps..

lukky replied on Tuesday, September 08, 2009

Well yes, I understand that in the BLB the items remain in their position, and this is why I'm using the LinqBindingList (through a Linq query) to "project" my List in a sorted way to the DataGridView.

The thing is, it works.... most of the time Confused [8-)]

So I'm digging into the code to find out why the BaseIndex of a ListItem (those names are taken from LinqBindingSource.cs) would be -1.

So far, there's only really 4 places where the BaseIndex is set, 2 of them being in the LinqBindingList<T>.SourceChanged event handler. The other 2 are in the BuildFilterIndex method. I'm suspecting the SourceChanged handler when the ListChangedType is ListChangedType.ItemAdded, because users have been reporting the error when creating a new Invoice and thus copying the details from either an Order or a Shipment BO (I have an overload on the InvoiceHeader that takes care of both use cases).

I'm still trying to find the exact use case that triggers this problem, and if possible create a bare bones example to reproduce it. I was hoping that either Rocky or Aaron would be able to help me understand under which circumstances the BaseIndex would be -1.

Regards.

ajj3085 replied on Tuesday, September 08, 2009

Right... my approach was to simply let the order be dictated by the original order of the list... and physically move items around in that list instead of relying on sorting by some property value.

lukky replied on Tuesday, September 08, 2009

Hi,

I'd really like to go to the bottom of this one.

As part of my investigation, as mentioned in my OP, I came across situations where the OriginalIndex of an Item is returned as -1.

The OriginalIndex property is as follows:

private int OriginalIndex(int filteredIndex)
{
      return _filterIndex[filteredIndex].BaseIndex;
}

So, it's really the BaseIndex of the ListItem being accessed that is returned as being -1. In what circumstances could this BaseIndex be -1 ? I'm trying to understand the code of the LinqBindingList, but at some point my head start spinning Embarrassed [:$]

I hope someone can help me shed some light on this.

Thanks.

Copyright (c) Marimer LLC