As you may know by now, I’m a big fan of Rockford Lhotka’s CSLA.NET framework and base classes. It’s packed with all sorts of cool functionality and I’ve used it for several years in various projects, but often I’ve found it too complex for smaller applications. In the newest version of CSLA.NET Lhotka uses Generics to add some nice features, but he could have pushed it even further. So, I’ve created another version of the BusinessBase base class, which I use in all kinds of projects.

The main difference is that my version is only 1 single class and not several .dll files packed with functionality that you probably never use. That makes it easy to customize to suit any particular need.

Here is the class definition where you can see the use of Generics and the Id property, which we will need later.

/// <summary>

/// This is the base class from which most business objects will be derived.

/// To create a business object, inherit from this class.

/// </summary>

/// <typeparam name="TYPE">The type of the derived class.</typeparam>

/// <typeparam name="KEY">The type of the Id property.</typeparam>

public abstract class BusinessBase<TYPE, KEY> : IDisposable where TYPE : BusinessBase<TYPE, KEY>, new()

{

 

  #region Properties

 

  private KEY _Id;

  /// <summary>

  /// Gets the unique Identification of the object.

  /// </summary>

  public KEY Id

  {

    get { return _Id; }

    set { _Id = value; }

  }

 

  #endregion

The new static method, Load(), takes care of loading existing objects based on their Id.

  /// <summary>

  /// Loads an instance of the object based on the Id.

  /// </summary>

  /// <param name="Id">The unique identifier of the object</param>

  public static TYPE Load(KEY id)

  {

    TYPE instance = new TYPE();

    instance.Id = id;

 

    if (instance.DataSelect(id))

    {

      instance.MarkOld();

      return instance;

    }

 

    return null;

  }

The base class handles a lot of things for you, so the implementation on derived objects can be kept to a minimum. Actually, you only have to implement 4 methods – methods you probably already use. It also includes some virtual methods to override for added functionality. I will not go through the code for the base class. You can download the BusinessBase class at the bottom. Instead, I want to show you how to implement it in a derived dummy class called Product. 

Implementation

Notice the Generics class definition, where you tell the base class what type the Id property should have – in this case an integer.

public class Product : BusinessBase<Product, int>

{

  #region Poperties

 

  private int _Price;

 

  public int Price

  {

    get { return _Price; }

    set

    {

      if (_Price != value) MarkDirty("Price");

      _Price = value;

    }

  }

 

  private string _Name;

  /// <summary>

  /// Gets or sets the name

  /// </summary>

  public string Name

  {

    get { return _Name; }

    set

    {

      if (_Name != value) MarkDirty("Name");

      _Name = value;

    }

  }

 

  #endregion

 

  #region Validation

 

  /// <summary>

  /// Reinforces the business rules by adding rules to the

  /// broken rules collection.

  /// </summary>

  protected override void ValidationRules()

  {

    AddRule("Price", "Price must be greater than or equal to 0", this.Price < 0);

    AddRule("Name", "Name must be set", string.IsNullOrEmpty(this.Name));

  }

 

  #endregion

 

  #region Data access

 

  protected override bool DataSelect(int id)

  {

    using (SqlConnection conn = new SqlConnection("connectionstring"))

    {

      string sql = "SELECT name, price FROM products WHERE productId=@id";

      using (SqlCommand cmd = new SqlCommand(sql, conn))

      {

        conn.Open();

        cmd.Parameters.Add("id", SqlDbType.Int).Value = this.Id;

        SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

       

        if (dr.Read())

        {

          this.Name = dr.GetString(0);

          this.Price = dr.GetInt32(1);

          return true;

        }

      }

    }

 

    return false;

  }

 

  protected override void DataUpdate()

  {

    using (SqlConnection conn = new SqlConnection("connectionstring"))

    {

      string sql = "UPDATE products SET name=@name, price=@price WHERE productId=@id";

      using (SqlCommand cmd = new SqlCommand(sql, conn))

      {

        conn.Open();

        cmd.Parameters.Add("name", SqlDbType.VarChar).Value = this.Name;

        cmd.Parameters.Add("price", SqlDbType.Int).Value = this.Price;

        cmd.Parameters.Add("id", SqlDbType.Int).Value = this.Id;

        cmd.ExecuteNonQuery();

      }

    }

  }

 

  protected override void DataInsert()

  {

    using (SqlConnection conn = new SqlConnection("connectionstring"))

    {

      string sql = "INSERT INTO products (id, name, price) VALUES (@id, @name, @price)";

      using (SqlCommand cmd = new SqlCommand(sql, conn))

      {

        conn.Open();

        cmd.Parameters.Add("id", SqlDbType.Int).Value = this.Id;

        cmd.Parameters.Add("name", SqlDbType.VarChar).Value = this.Name;

        cmd.Parameters.Add("price", SqlDbType.Int).Value = this.Price;

        cmd.ExecuteNonQuery();

      }

    }

  }

 

  protected override void DataDelete()

  {

    using (SqlConnection conn = new SqlConnection("connectionstring"))

    {

      string sql = "DELETE FROM products WHERE productId=@id";

      using (SqlCommand cmd = new SqlCommand(sql, conn))

      {

        conn.Open();

        cmd.Parameters.Add("id", SqlDbType.Int).Value = this.Id;

        cmd.ExecuteNonQuery();

      }

    }

  }

 

  #endregion

The 4 methods that need to be overridden are the data access methods, DataSelect, DataInsert, DataUpdate and DataDelete. You probably already have a version of these four methods on all business objects. The fifth overridden method is ValidationRules() and it is optional if you want to override it. In this method you can set all the different business rules that the object must adhere to. For more info about the validation feature, check this previous post on the subject.

Now, let’s look at the use of our Product class.

Usage

Let’s create a new product.

Product product = new Product();

product.Id = 657;

product.Name = "Carrot";

product.Price = 4;

product.Save();

Then let’s change the name of that object. Notice the static Load method.

Product product = Product.Load(657);

product.Name = "Tomato";

product.Save();

The power of the base class

All the derived classes are exposed to a lot of useful information that can be used to solve different problems. Here’s an example of an overridden Save() method, that takes advantage of the base class’ ability to track which properties have been changed.

public override void Save()

{

  base.Save();

 

  // If the Name property is changed, do something extra.

  if (base.IsPropertyDirty("Name"))

  {

    DoSomethingExtra();

  }

}

If you know CSLA.NET, then you also know that the Save() method is smart enough to find out if an object is new and must be inserted or old and must be updated. The save method also makes sure that you can only save a valid business object; otherwise it throws an exception, telling you what business rules are broken.

If you have thought about giving CSLA.NET a try, but decided it was to complex, then this could be a very good way to start using the same principles.

Customizing

I’ve implemented all sorts of different functionality that suited the particular projects over the years. Some of the coolest was caching, serialization and security. What makes it such a strong base class is the ability to expand and customize it as needed, and that versatility has serve me well in many projects. In fact, in almost all of the projects where I’ve used this base class, I’ve added some extra functionality to suit the unique needs of those projects.

Download

BusinessBase.zip (2,27 KB)

Comments


Comments are closed