Editable root list vs. editable root collection

Editable root list vs. editable root collection

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


voodoo45 posted on Monday, August 21, 2006

    I'm fairly new to CSLA in general and am helping to do some developement on a framework built off csla.  My confussion lies with root objects vs. collections.  In looking at the templates, it doesn't look like these two items will work together.  The Editable root's GetEditableRoot factory method takes an id where as the collection wants to pass in a datareader object.  Also, in the DataPortal_Update method of the collection object, it calls DeleteSelf, Insert, and Update all of which do not exist in the root object.  The reason I ask is that this is the way things have been set for implementation and I'm having a bugger of a time figuring out how things will wire up.  I noticed that the child collection is the same way.  If the editable root object were changed to an editable child object everything would work fine from what i can tell.  Is there no collection object that is meant to work directly with the EditableRoot object, or do I need to go through and make a bunch of changes to one of the collection objects to make this work?  Thanks for the help.

V

RockfordLhotka replied on Monday, August 21, 2006

Version 2.1 (which is in a test phase) includes a new class: Csla.EditableRootListBase. This class allows you to create a collection of editable root objects.

The collection ONLY loads the objects. Any insert/update/delete operations are handled on a per-object basis, because those objects are all root objects. The collection itself can never be saved, because it is merely a container for top-level root objects.

You can easily accomplish a similar thing by creating a simple collection class that implements DataPortal_Fetch(). However, ERLB goes a bit further, by automatically triggering insert/update/delete operations as you interact with the objects in the list through data binding. In other words, if you bind the collection to a DataGridView control, the user can add a new object by going to the bottom of the grid to add a new row, editing that row and then arrowing off the row. That arrowing off triggers an IEditableObject.ApplyEdit, which triggers a call to ERLB which calls Save() on the child.

The same thing happens with an update - edit data in a row and when the user navigates off the row the resulting IEditableObject.ApplyEdit triggers an automatic call to Save().

Similarly, removing an item from the collection automatically causes ERLB to mark the object as deleted and to call Save() - causing the object to immediately delete itself.

ERLB may not be the answer for all scenarios. But it handles the grid scenario, which is the complex one. Most other scenarios are simpler, and can be accomplished with almost any collection type and very little coding on your part.

Nicholas Trevatt replied on Saturday, January 06, 2007

I would appreciate it if someone could clarify the correct way to populate an EditableRootListBase collection and it's contained editable root objects. 

What I have done (and seems to be working ok) is create an InvoiceList (EditableRootListBase) which populates it's child Invoice objects (EditableRoot) by passing the Invoice the datareader.  But this how an Editable *Child* is populated hence my insecurity with the decision.  Is this the correct way?

Thanks,
Nicholas

Here is my InvoiceList.DataPortal_Fetch() method code.

Private Overloads Sub DataPortal_Fetch(ByVal criteria As Criteria)

        Me.RaiseListChangedEvents = False

        Using cn As New SqlConnection(Database.PrestoDBConnection)
            cn.Open()
            ApplicationContext.LocalContext("cn") = cn

            Using cm As SqlCommand = cn.CreateCommand
                With cm
                    .CommandType = CommandType.Text
                    .CommandText = "SELECT * FROM Invoices"
                    Using dr As New SafeDataReader(.ExecuteReader)
                        dr.Read()
                        Add(Invoice.GetInvoice(dr))
                    End Using
                End With
            End Using
        End Using

        If ApplicationContext.ExecutionLocation = ExecutionLocations.Client Then
            ApplicationContext.LocalContext.Remove("cn")
        End If

        Me.RaiseListChangedEvents = True

    End Sub


RockfordLhotka replied on Saturday, January 06, 2007

Nicholas, that looks right to me.
 
Rocky

Nicholas Trevatt replied on Saturday, January 06, 2007

Great.  Thanks for your prompt reply Rocky!

Nicholas

guyroch replied on Sunday, January 07, 2007

Not so fast guys...

You should have a try...catch...finally block to reset Me.RaiseListChangedEvents = True and to call ApplicationContext.LocalContext.Remove("cn") in case an error gets thrown. 

c#

try
{
   this.RaiseListChangedEvents = fasle;

   // Create you connection here

   // Add your connection to LocalContext here
   this.ApplicationContext.LocalContext("cn") = cn;

   // Do some more stuff here

}
catch
{
   // Handle errors here if applicable
}
finally
{
   this.RaiseListChangedEvents = true;
   this.ApplicationContext.LocalContext.Remove("cn");
}


This way your Me.RaiseListChangedEvents = True and to ApplicationContext.LocalContext.Remove("cn") will always get called.

 

AaronH replied on Thursday, April 01, 2010

I'm spinning my wheels here, running into all kinds of quirky behavior with the ERLB.  Unfortunately, I can't debug CSLA either to walk through to see what's going on due to some strange VS10 WCF behavior that I've previously posted about, and am taking a break from for the time being. 

So I have an ERLB.  I have a SL ListBox bound to it.  There is an "Add" button that pops up a popup form.  What should the workflow look like?  Let's exclude MVVM to avoid complication.

So for example:  view (page, uc, etc.) instantiates a new editable root and invokes BeginEdit().  Pass it to the popup form (via constructor, etc.).  If user clicks "OK" button on popup, the underlying view adds the new editable root to the ERLB via Add().  Then, what happens next?  Does the view invoke ER.ApplyEdit()?  Does the view invoke ERLB.Save()?  This is where I'm getting lost and why I suspect I'm running into issues.

Also, in the case of an Edit scenario.  The user selects a item in the ListBox, grabs the bound ER, the view invokes BeginEdit().  User clicks "OK" button on popup, then ER.ApplyEdit() is invoked.  OR user clicks "Cancel" button and ER.CancelEdit() is invoked.  Is that all that needs to happen?  Do I need to inform the ERLB of any of this?

I've looked through the samples that came with the CSLA SL vids and have modeled my ERLB the same way, but I'm not consuming it in the same fashion as the demos are, so running into issues there.  Also, the book is unclear on consumption patterns of the ERLB.

Thanks!

RockfordLhotka replied on Thursday, April 01, 2010

If it hasn't been said before in this thread, ERLB is designed for one thing and one thing only: to support in-place datagrid editing where you want changes to a row to be saved as the user leaves that row. Any other use of the ERLB requires that you emulate data binding as expected by the ERLB.

Typically though, emulating data binding isn't too hard, since the ERLB expects very little. In fact, it just requires this:

  1. Before the user edits an object in the list, you must call BeginEdit() on that object
  2. When the user is done editing an object in the list, you must call CancelEdit() or ApplyEdit() on that object
  3. Only one object at a time may be in edit mode
  4. To add a new item to the list, call AddNew() on the list - if the user edits this object, items 1-3 apply
  5. To remove an item from the list, call Remove() or RemoveAt() on the list - the item should not be in edit mode when this is called (iirc)

These are all things a datagrid control does automatically through data binding. If you aren't using a datagrid control, then you need to do these things.

You can do them in your code-behind or viewmodel, but you need to emulate the actions of a datagrid.

 

AaronH replied on Thursday, April 01, 2010

Great, the edit part works awesome!  However, ERLB.AddNew() doesn't seem to return a type.  I'm using a VB.NET SL client, and its return type is Sub.  This is strange, because if I dig far enough down the hierarchy, I see that AddNew() is defined on BindingList<T> as a method that returns type T.

What gives?  If you have any immediate thoughts, please let me know.  I'll keep digging on my end.  It's pretty strange though...

RockfordLhotka replied on Thursday, April 01, 2010

I just answered this, though maybe in a different thread.

AddNew() doesn't exist as a concept in ObservableCollection. It did and does exist in BindingList, and it is a useful concept. So we added it to our OC in CSLA - so all the CSLA collections have it.

But in SL server access is async. Often AddNewCore() needs to call DataPortal.Create() in .NET - but that's BeginCreate() in SL - so AddNewCore() must be async in SL.

(as an aside, this was how we spent much of 2007 - add one async feature for SL and find that you need to a others as a result - quite the cascade effect!!)

So AddNewCore() in SL is different from in .NET, because it must support async. It doesn't have to actually be async, but it needs to follow the async pattern in case you want to use the data portal.

This means AddNewCore() is half the equation, and OnAddedNew() is the other half - the part that is invoked when the add is done.

A typical SL add new looks like this:

protected override void AddNewCore()
{
  var item = new Item();
  Add(item);
  OnAddedNew(item);
}

If that needs to be async, you just change it so the Add(item) and OnAddedNew(item) calls occur in the async callback - such as the CreateCompleted event handler from the data portal.

btw, this is covered in the CSLA .NET for Silverlight video series, along with code examples.

AaronH replied on Thursday, April 01, 2010

Honestly, the videos are a bit difficult to find specific information due to no indexing.  It's hard to keep track where something was covered, and I just don't have the time to sit there and watch an entire video again to grab that one nugget of information unfortunately.  Don't get me wrong, the videos are great, it's just difficult to remember where everything was covered (video number and exact minute)

In addition, the samples that come with the vids do not use the ERLBs like I'm trying to use them.  It's not creating the ERLB that is causing me the grief, it's manually manipulating the collection itself and the items contained within that is (due to lack of understanding how it works underneath the sheets, admittedely).

I know you said in a previous post that the ERLB is meant to be used with a datagrid, however, I need to quote you from chapter 5 in your latest book:

At times, applications need to retrieve a collection of child objects directly. To do this, you
need to create a root collection object. For instance, the application may have a WPF UI
consisting of a ListBox control that displays a collection of Contact objects. If the root object
is a collection of child Contact objects, the UI developer can simply bind the collection to the
ListBox (with an appropriate data template), and the user can edit all the objects in the list.

This is exactly the approach that I have decided to take (only with an Accordian), so it is important that I invoke methods explicitly against the ERLB.

Anyhow, thanks for all of your help.

RockfordLhotka replied on Thursday, April 01, 2010

I was just saying that the AddNewCore/OnAddedNew code is in the samples with the videos.

Your use of ERLB outside a datagrid is fine, but is not a mainstream scenario, so I wouldn't cover it in a book or video - unless it was an advanced tips and tricks book/video.

JoeFallon1 replied on Tuesday, August 22, 2006

voodoo45:
    I'm fairly new to CSLA in general and am helping to do some developement on a framework built off csla.  My confussion lies with root objects vs. collections.  In looking at the templates, it doesn't look like these two items will work together.  The Editable root's GetEditableRoot factory method takes an id where as the collection wants to pass in a datareader object.  Also, in the DataPortal_Update method of the collection object, it calls DeleteSelf, Insert, and Update all of which do not exist in the root object.  The reason I ask is that this is the way things have been set for implementation and I'm having a bugger of a time figuring out how things will wire up.  I noticed that the child collection is the same way.  If the editable root object were changed to an editable child object everything would work fine from what i can tell.  Is there no collection object that is meant to work directly with the EditableRoot object, or do I need to go through and make a bunch of changes to one of the collection objects to make this work?  Thanks for the help.

V

 

FYI - the VB templates are not 100% complete (or correct).

I think the C# templates are maintained by Rick Supit and are in better shape.

You can also check out:

http://www.onelittlevictory.com for sample BOs and a basic discussion of each region.

 

Joe

Copyright (c) Marimer LLC