This is related to post http://forums.lhotka.net/forums/p/4056/19883.aspx#19883
The book (Expert C# 2008 BOs, page 150) under Child Object Creation, says
"Child objects are usually created when the UI code calls an Add() method on the collection object
that contains the child object. Ideally, the child class and the collection class will be in the same
assembly, so the static factory methods on a child object can be scoped as internal, rather than
public. This way, the UI can’t directly create the object, but the collection object can create the child
when the UI calls the collection’s Add() method.
The CSLA .NET framework doesn’t actually dictate this approach. Rather, it’s a design choice on
my part because I feel that it makes the use of the business objects more intuitive from the UI developer’s
perspective. It’s quite possible to allow the UI code to create child objects directly by making the child
factory methods public; the collection’s Add() method would then accept a prebuilt child object as
a parameter. I think that’s less intuitive, but it’s perfectly valid, and you can implement your objects
that way if you choose."
(Note: in this discussion, I'm not worried about databinding so I don't need to do anything with AddNewCore())
So, it seems that the preferred way of creating a child object is to implement an AddSomething() method
on the child collection object. It seems that the place to put that method would be in the
Business Methods region. However, neither the VS templates nor the book (pages 190-191) show the collection templates as having a Business Methods region which seems to suggest that it isn't necessary or preferred.
Obviously it's easy to add a new region and implement whatever I want but this seems to be a contradiction, especially for one just learning the framework and trying to figure out how do things the 'Rocky' way as much as possible.
So, if implementing an Add() method on the collection object is preferred, why is there no Business Methods region in the template? Or, if you should avoid putting Business Methods in collection objects, then what is the preferred way of adding a child to a collection?
Or am I missing something?
I don't know if there's a "preferred" way.
I have my preference, but other people have their preferences.
If you look at .NET, you can see examples where the caller is expected to create the item and add it to the list:
var child = new Child();
There's nothing wrong with that - though in a CSLA setting you'd do this:
var child = Child.NewChild();
This works great if the newly created child doesn't need any contextual information from the parent in order to be created, and that's often the case.
Another variation on this theme, that works with Windows Forms datagrid controls, is auto-adding of items to a collection by overriding AddNewCore() and setting AllowNew=true in your collection. Then the caller (often data binding) just calls myList.AddNew() and a new item appears at the end of the list. Very smooth, especially when using in-place editing in a datagrid.
I use the AddNewCore() quite a lot - it is a nice technique.
Finally, you can do what I tend to do frequently, which is to create an Add() method in the list that creates the child, adds it to the list and returns a reference to the new child. I only really like this because it simplifies the UI code - the caller doesn't need to know about the child object's factory method, and in fact the child object doesn't need a public factory at all.
I just think it makes the overall API simpler and easier to consume.
But not everyone agrees, and that's fine - all three of these techniques are fine, and they aren't mutually exclusive.
Thanks Rocky for the quick response. I suspect the post will get hit a lot by people trying to figure this stuff out as it concisely provides three examples of ways to create child objects.
When I say 'preferred' in a CSLA context, I do mean Rocky's preference. Anybody who is willingly using CSLA has already accepted that Rocky's decisions are usually good decisions and so the newbie CSLA programmer will often accept his preferences on faith (even if he/she doesn't understand it completely) in order to avoid having to spend a bunch of time researching all the various ways of doing things. In my limited experience doing CSLA work in a team environment, pretty much any time there is a design decision to be made the first thing we ask ourselves is, "What would Rocky do?". If we can figure out what Rocky would do we usually end up deciding that it is the right (or best) thing to do.
Usually (pretty much always), my child objects have a foreign key that points back to the parent object in the database. The foreign key could be set by the UI but my preference is that the UI shouldn't have to know anything about foreign keys (and they certainly should not be writable) so it should get set automatically when the child is created. With this decision in mind, creating a rogue child and then adding it to the collection (using the default .Add(ChildObject item) method) doesn't seem very clean. I think the same holds true for using AddNewCore() although since you have to override it anyway, it wouldn't be too hard to add the foreign key property in there.
I guess then, that I too prefer to create custom Add() method(s) in the collection object which will ensure that the foreign key (and any other parental context information) is automatically set when the child is created. The question is still open then...
"Why don't the collection templates have a Business Methods region?"
For your specific problem, I would think the root object would have the key needed by the child objects. It would hold that value until a DataPortal_Insert or Update, and then pass it as one of the parameters to update the children.
The child doesn't need the key at all until that point, and it would just accept whatever its given. If the root is the collection though, you have to figure out where to store the key, but I think it would be unusual that a collection like that would be designed.
This seems very reasonable, except that the child object is switchable. It also opens the possibility that the root might write the child object out without the required data. It was interesting how many bugs I found in our legacy code by virtual of the translation when all the child objects started enforcing their own rules in the new software... :)
Hello, I know this is an old topic, but could someone(Rocky) post an example of how to implement the ItemList.Add() method.
As I mentioned earlier in the thread, there are several ways to add a child item to a collection. I cover the three most commonly used techniques in the Using CSLA 4: Creating Business Objects ebook.
To answer your specific question - one technique does use the Add method that exists on all collections. No implementation in the collection is necessary, because the Microsoft base classes already implement an Add method. You just need to create a child object to add. I often use a unit of work command object for this purpose, so your code (assuming you have the uow object) looks like this:
var child = EditableChildCreator.CreateChild();
var child = EditableChildCreator.CreateChild();
The EditableChildCreator type is the uow command object that correctly creates and initializes the child, then you just use the pre-existing Add method on the collection.
I use both methods, but since I do edit collections in grids and want the grids to be able properly add objects, I've made sure that AddNewCore() cooperates and have implemented overrides where necessary.
The main situation that prompted this is that it is very often the case that child objects in a collection have foreign key references to the parent of the collection. The child objects are actually broken in my case unless these values are provided.
Another thing I have found to be the case is that some grids are *very* finicky about list changed events being fired while they are initially adding a brand new item to a list, so I'm sure to do any property initialization before the item is actually added to the list.
So, I usually let the collection do the adding unless I have lots of my own initialization to do afterwards.
Copyright (c) Marimer LLC