I am a bit disappointed that the book does not delve into a better explanation why we should be using the custom CslaDataSource versus the built-in ObjectDataSource. As comprehensive as most of the explanations are, this left me at a loss.
I've done quite a bit of reading and playing with the ObjectDataSource and am struggling to understand why implementing a custom equivalent is better. I always tend to rely on built-in before custom and, frankly, at first glance the CslaDataSource looks like it requires significantly more coding by the end-user/developer than using an ObjectDataSource.
I'd like to open a comparitive discussion for those of us struggling to understand the merits behind the custom approach. Specifically, what short-comings in the ObjectDataSource are we overcoming with the custom CslaDataSource that justify its implementation??? It is obvious from searching & scanning the forum that this approach is being pursued widely with a number of issues including paging and sorting which are provided for in the ObjectDataSource.
I am open to this and believe that Rocky tends to have solid rationale behind the approaches used in his framework, unfortunately, this is not explained very well in the book. Any help understanding why we should go this route is appreciated.
The ObjectDataSource requires that you bastardize your object model to conform to its set of requirements. These include:
Now these are all achievable goals. But not if you want to have any hope of creating an object model that is useful in any other context (like Windows Forms or even behind a Web Service).
And these goals are very hard to achieve with CSLA .NET, because it is designed around a different set of principles.
To use the ObjectDataSource with a CSLA .NET object, you'd need to make the constructor public, and then compensate as needed within your object code because the factory methods are no longer the only way to create an object. This isn't overly difficult, but there are some edge cases that are tricky.
Then you have to avoid all use of read-only properties. So even your primary key value (which should be immutable) must be changable. Moreover, you can't block any values from being set or read in any property - so you can't use the authorization support in CSLA. Any blockage of a get/set will cause the ObjectDataSource to fail, causing your page to fail.
Finally, your object needs to do a "blind update", where it accepts that all data put into the object is old data, even though the object is new (because ObjectDataSource creates it to do the update). I did add framework support for this through the Save(forceUpdate) overload.
But perhaps most important is this: all your objects must be editable root objects. You can't have any child objects. So any behaviors that would naturally fall within the context of a collection or a parent object must be implemented within the child object.
As an example, you might have rules about OrderLineItem objects controlling total sales or some discount percentage or other things that require looking at all the line items to determine the right course of action. When using the ObjectDataSource, each OrderLineItem must be a standalone editable root - because data binding will update those rows in an atomic manner. This means that the OrderLineItem class must contain all the code for a line item, PLUS all the code for an Order, so it can do the right thing while being saved.
Obviously you can normalize this behavior and make your code maintainable. But this type of object model is much more expensive to create and maintain than a "real" object model where the very relationships between objects naturally enforce quite a lot of the rules inherent in your use cases.
So in short, it is possible to create bastardized objects that do work behind ObjectDataSource, as long as you are willing to pay the price in terms of increased complexity and decreased functionality from CSLA. Or you can use the CslaDataSource, which doesn't impose these conditions on your objects, but does require you to write an extra few lines of code to copy the data.
I had though, btw, of doing what ObjectDataSource does, and use more aggressive reflection to load the data into the objects. But I already get so much flak for my use of reflection that I thought I'd just create DataMapper and then leave it up to you to decide whether to use reflection or not. With ObjectDataSource you automatically use tons of reflection and have no choice - perhaps I should have just taken that approach as well...
Rocky,
Thanks for the prompt & thorough reply. This certainly addresses the issues and gives me a better understanding of the approach - and why. I will look closer at that section in the book and the code now that I have a grasp on what's "wrong" with the ObjectDataSource.
My concern, as you eluded to, is/was based on the MS premise that the 'x'DataSource objects were designed to make the UI code free of any rigorous data-binding code. And, working in a distributed team environment makes this concept appealing so my UI guys aren't screwing up functionality (form vs. function, ya know?). That was why the event handlers you described seemed counter-productive. But, with this explanation I understand their purpose and benefit.
I am wondering what your thoughts are, however, of finding a way to accomplish both approaches and design the CslaDataSource to behave by default like the ObjectDataSource in that it would use reflection to eliminate the need for the UI code but provide means for the developer to override, or cancel, this behavior and implement code like you've described sans reflection?
Obviously, the typical approach of virtual protected methods won't work because we aren't expected to derive from CslaDataSource, but perhaps making use of CancelEventArgs? An approach that I have taken previously that comes to mind now is in the protected method that raises the event, such as OnSelectObject, when we check to see if an event handler is defined (SelectObject != null), add an else clause so that we raise the event if the user/developer has indicated a handler otherwise, if no handler is defined, we proceed with the default implementation. So, we would have code similar to this:
internal void OnSelectObject(SelectObjectArgs e)
{
if (SelectObject == null)
{
// Default process using reflection
}
else
SelectObject(this, e);
}
I'm not sure this would work in this case and maybe the default code should be in the ExecuteSelect method of the CslaDataSourceView class? If args.BusinessObject is null, then the event wasn't handled and no object was created so use reflection to do so(?).
What do you think? Is this more work that it's worth or do you think there's a way to accomplish this so we can have the "best of both worlds"?
Thanks in advance.
Rocky,
This is why you write the books and I buy them!
Kevin
Copyright (c) Marimer LLC