Design issue with parent-child setup

Design issue with parent-child setup

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


wjcomeaux posted on Wednesday, September 17, 2008

Ok, so we have this application with a constraint that data only be loaded in a single instance.

We have an editable root object (Employer) that has an editable child collection TaxNotice.

Each TaxNotice has an editable child collection TaxNoticeAction.

We want this to be a tab interface where the Employer tab displays it's information and a listbox containing all of its TaxNotices. Clicking on a TaxNotice row causes the form to switch to the TaxNotice tab and passes the current object in the binding source like this

if (this.ParentForm != null && ((FormMain)this.ParentForm).SwitchTab("tpTaxNotice"))

{

((ucTaxNotice)((FormMain)this.ParentForm).GetTabPageUserControl()).PayrollEmployerTaxNotice_EC = (PayrollEmployerTaxNotice_EC)payrollEmployerTaxNotice_ECLBindingSource.Current;

}

I have exposed the functions SwitchTab and GetTabPageUserControl on the main form so that each user control can cause a tab switch where appropriate.

All of the switching works correctly, however when I bind the datasource on the TaxNotice user control and make changes I am not able to have those changes reflected on the parent form. Also, being it's an editable child I'm not able to persist at the level of the child user control.

Obviously, I don't want to persist from the very top level (Employer) just to save a change at the child level, however we also don't want to load an EditableRoot object at the child level, persist and then force the parent to refresh its view from the database. I think persisting from the top level might be my only option though we'd really like to conduct more fine grained persistence.

Is it feasible to pass a reference of the parent object to all children and call Save on the parent when child edits are done?

One of the requirements is that the user should not be able to tab away from a dirty object so if something is changed they either need to Apply or Cancel the edit before leaving the tab.

Also, I notice that even though I am passing the datasource by reference, the parent control never seems to see the update, even when I think it should.

For instance, if I allow tab switches on a dirty object I can change the child objects name or ID and not see the change reflected on the parent object. Do I need to refresh bindings on the parent for this to happen?

What options am I overlooking?

Thanks,

Will

RockfordLhotka replied on Thursday, September 18, 2008

If I understand correctly, you want the same object displayed/edited on multiple forms or tabs?

This is very complex, thanks to the way data binding works.

Each time you bind an object to a bindingsource, that bindingsource thinks it has exclusive control over the object. If it doesn't have exclusive control over the object you'll get inconsistent results.

So you must ensure that a bindingsource has exclusive control over the object - which means that you must unbind the object (see the Using CSLA .NET 3.0 ebook) from the previous bindingsource before binding it to the next one.

Or, you must disable IEditableObject support in the business object - that might work too, though I've not tried this scenario. But the current version of CSLA allows an object to disable its IEditableObject support - for scenarios where data binding's behaviors just can't be worked around. This might be one of those scenarios...

wjcomeaux replied on Thursday, September 18, 2008

Thanks Rocky:

After some more testing yesterday I kind of came to the same result though I couldn't figure out why. I was attempting to set the datasource on a "child" control by passing the current datasource from the parent. The end result was as expected, the child could update itself but the parent had no clue about the changes. I was thinking it would work like a simple pass by reference where change to a value in one location would be reflected back to the other. Turns out with databinding this isn't really possible.

So, now I'm off to debate this position and come up with a design that might work. :)

Thanks again,

Will

stefan replied on Thursday, September 18, 2008

What about passing around the BindingSource...
Haven't tried that yet, but you could do, and post your results! Smile [:)]

Stefan

wjcomeaux replied on Thursday, September 18, 2008

Well that's actually the first direction I was going initially and couldn't get it to work.

I had a binding source on each user control that I exposed as a property. Then I'd have the parent control set the bindingsource.DataSource on the child.

The child gets updated normally but the parent doesn't see the changes. This was another case of bad assumption on the passing of a datasource.

However, both of my approaches suffer from the fact that only the root object can be saved so I wouldn't be able to persist a single small change without going through the root. And in this case the object graph could get pretty large so it's not really worth it.

This does beg the question of how to accomplish such a thing.

Here are my most basic needs.

1)ParentForm with editable data and a listbox containing child ids.

Click on a child id in the parent listbox and the app switches tabs to the child tab.

Make some edits on the child tab and click save. Child is persisted without having to call save on the parent. Parent is notified somehow of the change to the child.

N levels of depth for parent-child-grandchild...(n likely being 6-7 levels) some branches containing 10,000+ children.

Don't allow the user to leave a tab that has dirty data (save or cancel is required for this particular UI).

Other factors can change outside the scope of local user edits. For instance, the user can be working at the child level and an external message can arrive that causes updates a grandchild record. (Sockets application where external client app could do something like "request grandchildX change name to Bob")

Complicated eh?...

Will

wjcomeaux replied on Thursday, September 18, 2008

Ok, I fixed it. It made absolutely no sense that binding to a reference of a datasource wouldn't update all references then it hit me. The listbox I show on the parent form for testing purposes was displaying the primary key and the child form had the primary key textbox in editable mode. So I could change the text but CSLA was ignoring. And being this is RAD purposes there isn't much for error handling and validation. So CSLA quietly ignored the change to the primary key (as it should) and I confused myself when the parent didn't update.

So, big problem solved. Now all that's left is to address the issue where we would like to save from the child level but I'm thinking there might be a way to have CSLA simply keep track of dirty objects.

What we want to avoid is the possibility of code that does this (the default CSLA behavior)

foreach child // many times

   ifDirty() foreach grandchild // thousands of times

      ifDirty() foreach greatgrandchild // thousands * thousands of times

Has anyone ever investigated a save option that guarantees only dirty objects are saved? I'll search the forums now. :)

Thanks for the moral support.

Will

P.S. This makes the 3rd large company that I've brought into the light of CSLA with CodeSmith and they all love it, though they've all brought up interesting and demanding uses for it...

Fintanv replied on Friday, September 19, 2008

What about the idea of implementing some sort of child tracking functionality in the root object of your hierarchy.  Maybe by keying off the ChildChanged event?  (I believe as of 3.6 this event will bubble all the way up the chain).  When save is called you would then only check the 'tracked' children and handle their saving on a case by case basis.

Copyright (c) Marimer LLC