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.

Comments


Comments are closed