Last week, Rocky Lhotka returned to the .NET
Rocks! online radio show with Carl
Franklin, announcing the release of his CSLA.NET
2.0 business object framework. This is exciting news for anyone who uses the framework
today. I was introduced to CSLA.NET back in the early years of .NET and has been hooked
on Rocky’s fantastic idea behind business objects ever since. I’m not underestimating
when I say, that he’s books and the CSLA framework changed the way I design software drastically
ever since I read the first book.
So, you can imagine my excitement when the news about the new release came to my attention.
From the previous versions, he has now gone the whole nine yards in extending the
platform to the .NET Framework 2.0 in a variety of ways. This was not possible before, he explains, because of the backward compatibility issues with current users. But
those users had to upgrade to .NET 2.0 anyway, so he figured that it was now or never.
The features that has undergone most changes are
- Validation
- Authentication
- The data portal
- The use of Generics
At Traceworks, I've been designing the business
logic framework based on CSLA for .NET 1.1, but I wrote it in C# 2.0 for about a year
ago. It was necessary to extend the original framework substantially to take full
advantage of the new capabilities of the .NET Framework 2.0, especially Generics.
We also implemented an extended validation mechanism and authentication store.
When I downloaded the
new CSLA release, I found that Lhotka had been using almost the same way of implementing
Generics and Authentication as we had. The validation mechanism is a bit more advanced
in Lhotka’s release and I want to implement it when I have the change. Brilliant work, no question.
The most interesting feature IMO is the implementation of Generics on the BusinessBase
base class. As I said, it is much like the implementation I did a year ago, and it
has worked perfectly. There’s one thing missing in Lhotka’s release though, and that’s
a Generic load method. That’s the coolest feature of .NET Generics by far. It lets
you implement a Load() method on the base class that automatically instantiates the
inherited class by calling its virtual DataPortal_Fetch (we call it DataSelect, but
the idea is the same). It's completely strongly typed, and it is only possible through
Generics.
Let's extend Lhotka’s BusinessBase class definition with new() at
the end like this:
The CSLA.NET 2.0 definition:
public abstract class BusinessBase<T> : Core.BusinessBase where T : BusinessBase<T>
The Traceworks definition:
public abstract class BusinessBase<T> : Core.BusinessBase where T : BusinessBase<T>, new()
The new() tells
the BusinessBase that a public default constructor must be implemented by all derived
classes, and thereby makes the base class able to instantiate the inherited class
without the use of reflection and in a strongly typed fashion.
That allows us to implement the Load(Guid id) which returns whatever business object
that inherits the base class. It could be done like this:
public static T
Load(Guid id)
{
// Creates an instance of T which is the derived class
T instance = new T();
// Fills the properties of the instance with
//data from the data store.
instance = instance.DataSelect(instance, id);
// This is where the authentication and
// permission (read-only, admin, deny, author) is set.
// It's our custom implentation, so I won't go into details.
// ...
// Mark the instance Old
instance.MarkOld();
// Fires the public Load event
this.Onload();
return instance;
}
This way it is possible to create an instance of any derived class like this:
DerivedClass
dc = DerivedClass.Load(new Guid("12E1325A-4E54-4909-921C-6835C8C9C0CD"));
Notice that the instance is calling its DataSelect() method and adding itself as a
parameter. This is not necessary to do for this method alone to work, the DataSelect()
could just as easily use itself and not the provided parameter of itself. The reason
is that the derived class is then able to create instances of the same type as itself.
You can leave that option out if you like.
This doesn’t rule out the possibility to use parameterized constructors in the derived
class at all if you prefer that approach. You can have both, but I think the best
way is using the Load() method at all times. You would still use the constructor to
create new objects and then hit the Save() method. Again, it is a custom implementation, so it may not suit all needs.
Notice the use of System.Guid as the id parameter of the Load() and DataSelect() method.
We decided from the beginning to implement a Guid Id on the base class. It was a tough
discussion with good arguments both ways, but finally we went with the Guid implementation.
That’s one decision we haven’t regretted for one moment ever since. Here is the
Id property on our base class:
private Guid
_Id = Guid.NewGuid();
public Guid
Id
{
get { return _Id; }
protected set { _Id = value; }
}
The property’s setter is protected so only a derived class can set it. It’s a new
feature of .NET 2.0 to have different accessibility to a property's getter and setter.
Cool stuff.
By placing all these kinds of decisions on the base class, we have very clean derived
classes. They only take care of the O/R mapping, business rules, and logic and nothing
else. Our version of the BusinessBase now takes care of Validation, Serialization
(strongly typed using Generics), Caching, Authentication, Events, and the rest of
what CSLA offers of functionality.
As you can see, our implantation of a Generic Load() method is pretty simple, but
yet extremely powerful. It provide a consistent way of pulling business object from
the data store and makes the implementation of the derived classes much, much smoother.
* $4.95/month BlogEngine.net Hosting – Click Here!