I'm having a problem with what seems to be a case for a switchable object. The apparent problem is that an otherwise root object is being treated as a child object when added to a collection, and it doesn't need to be treated as such.
To illustrate, I have a Book that inherits BusinessBase(T) and a BookList that inherits BusinessListBase(T, Book). Books are otherwise root objects, with BookList serving primarily as a bindable collection for use with the BindingSource control designer, but also as a factory for obtaining various filtered collections of Books. There is no need for a collection of Books to be owned by a parent object of any kind.
My program displays a BookListEdit screen where a user can not only edit Books in a grid, but also add or remove Books, all of which is cancelable.
To create new Books from the BookListEdit screen, a new Book object is created (it will then be a root) and displayed in a BookEdit screen. If the user accepts the edits, the new Book object is added to the BookList collection, and control is returned to the BookListEdit screen. The user can then save or cancel any pending edits.
All of this works except for a single case: an error is thrown when a new Book is removed from the collection before saving. The exception is thrown in BusinessBase.DeleteChild, and is: NotSupportedException: Invalid for root object - use Delete instead. Keep in mind that the exception is thrown when BookList.Remove(aBook) is called, well before any save operation is attempted.
After looking it over for awhile, here are things I've considered to do and not do:
- Let the Book exist as a root, but mark it as a child if it gets added to a BookList. This is my preferred approach, and I'm doing it by:
a) Adding a internal Book.MarkAsChildInternal() method to have Book call its own MarkAsChild() method.
b) Overriding InsertItem() in BookList and calling Book.MarkAsChildInternal().
- Use a different base class for BookList, one that doesn't care about root or child status. I like the CSLA infrastructure, so I'd rather use one within the framework.
Approaches not taken:
- Change the use case. Adding, editing, removing Books are all valid operations from a usability standpoint; they shouldn't have to change.
- Implement BookList.AddNew(). This breaks the ability for Book to be a root object, and unnecessarily modifies the BookList collection during the new Book edit.
An interesting observation: All other CRUD operation scenarios are working, it's only the case of adding then removing a new Book before a save.
Can anyone add to this discussion, or offer an alternative?
"Switchable" objects are only switchable when they are first created - that's the time you have to decide if it is a root or child.
A BusinessListBase collection can only contain child objects. If you put a root object into a BLB you'll run into trouble - which you have. There's actually an item in the to-do list to add a check and throw an exception when a root object is mistakenly added to a BLB - it just hasn't floated to the top of the queue yet, because this isn't a terribly common issue.
If you want a collection of root objects, use ERLB - though that comes with a set of other consequences (like auto-saving of root objects based on data binding interacting with the collection).
There is no collection type that works with root and child objects. That's not really possible, since the data access implementations are different between the two (DataPortal_XYZ vs Child_XYZ), and there's no way to know you did both sets of data code for any given object, nor would there be a clear way for the data portal to know which to invoke.
I nearly always recommend against the use of a switchable object - they always introduce complexity. In my view it is almost always better to have two types for the different responsibilities.
In your case however, you might consider having two parent/root types instead - thus allowing Book to always be a child. For your collection case you already have what you need. Just create another parent - another BusinessBase, or a command object - that can save that one child for the single-object scenario. Once it is saved, just add it to the collection and discard the old parent (I haven't actually tried that - but it should work fine).
Thanks for the reply and the suggestion to use ERLB; I was previously unaware of that class.
I changed over to ERLB and it worked ok, but I'm not a fan of the auto-save, as you mentioned in your reply. I like the interaction model of BLB, where edits are saved/canceled by the user (or me, the developer!). However, the "child" problem was getting in the way of BLB, even though I really don't Books to be child objects (but BLB requires them to be children). Because of this, all things considered, I've decided to punt and go with my original workaround, which is to have the collection mark the Book as a child whenever one is added to the collection.
I was thinking more about this problem last night (before I had read your suggestions) and it seems to me that an object's "child-ness" is determined by virtue of being added to the collection, not the manner in which its loaded (e.g. constructor that accepts a DataReader), and that the collection's marking its elements as children makes sense. But this is only one aspect of the parent-child relationship, and there could be other consequences of doing this.
Thanks again for your input, Rocky!
Copyright (c) Marimer LLC