An Introduction to Business Objects in C#

Environment:
Visual Studio .Net, C#, Windows 2000, Windows XP Professional

Keywords:
Business Objects, C#, Windows Forms, Custom Events, Database Access, ADO.NET,
Web Forms, ASP.NET


This sample is an introduction to Rockford Lhotka’s Component-based Scalable Logical Architecture (CSLA) as described in his book, Visual Basic 6 Business Objects (Wrox Press, 1998). The CSLA is an implementation of n-tier design, where an application is logically partitioned into four layers – presentation, UI-centric business objects, data-centric business objects, and data services. The purpose of the architecture is to achieve two overarching objectives.

  • Scalability.

  • Flexibility in the physical placement of each layer.



    The latter means that we can decide whether to place all the layers on a single machine or to distribute them between client and server machines with little or no impact on the business logic code. It also means that we can provide a variety of presentation layers, ranging from a traditional Windows interface to a Web interface, with little or no impact on the business logic code.


    A typical physical architecture, utilising a rich Windows-style user interface, is to place the UI-centric objects on a client workstation where they are close to the user interface. The UI-centric objects communicate with the data-centric objects, which are placed on an application server, and the latter communicate with the data services, which are on a database server.


    The code presented here in C# is not a full implementation of the architecture. Rather, it serves to illustrate the basic ideas. It presents a single UI-centric business object that captures basic information about a person – social security number, name and date of birth. It shows how a Microsoft .Net Windows Forms user interface can interact with the business object while allowing the latter to validate all the business rules as the user enters data. The user interface is completely insulated from detailed business rule validation. It just needs to act on a property of the business object – whether it is currently in a valid or invalid state.


    UML Class Diagram


    The Person Class



    The Person class has three supplied properties:


  • Social Security Number

  • Name

  • Birth Date



    and one computed property:


  • Age



    It has three business rules.


  • Social Security Number contains exactly 11 alphanumeric characters (though the
    sample asks the user for digits).

  • Name must be non-empty.

  • Birth Date must be in a valid format.



    A Person object is not considered to be in a valid state until these rules are
    satisfied.


    In order to provide the richest and most responsive user experience the idea is
    to validate these rules while the user is entering their data, rather than
    after it has all been entered. Other user interfaces, such as an HTML 3.2
    interface, can make do with a “batch”-style interface but we want to be able to
    use a variety of user interfaces and a Windows-style GUI provides the richest.


    The basic idea is that the controls for saving the user’s data should only
    become available when the user has supplied all the data necessary to make a
    Person valid. It is the user interface developer’s job to write the code to do
    this. But they don’t need to write detailed validation code they need only
    query the Person’s “Valid” property.


    However, in practice, the UI developer has to know more about a Person than
    this. But what they have to know is not to do with business rules but with the
    operational state of a Person at any time.


    Let’s consider the operations that can be performed on a Person:


    Add a new Person.

    Save it to a database.

    Load an existing Person from the database.

    Delete a Person.


    A rich user interface will have features that enable a user to:


    Edit their data, save their changes and close the application.

    Edit their data, save their changes, do further editing, save and close.

    Edit their data, cancel their changes, and close.


    And so on.


    In a data entry form the above is typically implemented using OK, Cancel and
    Apply buttons. This might not seem particularly problematic. However, a feature
    of this architecture is that the Person object’s state is being updated as the
    user types. If the user cancels their edits it is important to restore the
    object to the last valid state it was in prior to the edits. This means we have
    to keep a copy of the current state after each Apply operation.


    In this particular example, this doesn’t matter but in a more complex
    application, say, with collections of Persons, it would. And we are trying to
    illustrate the general principles.


    The UI developer also needs to know a few other things about a Person, such as
    whether a Person is new, modified (dirty), or is currently being edited.


    For example, it should not be possible to load a new Person from the database
    while one is being edited.


    Managing Edits



    To facilitate editing, three methods are provided:


    BeginEdit


    This enables editing.


    ApplyEdit


    Saves or deletes object if appropriate and terminates editing. Clients should
    immediately call BeginEdit if they wish to continue editing.


    CancelEdit


    Cancels all changes since the last ApplyEdit operation or since the object was
    marked for deletion. Terminates editing.


    In database terminology these three methods function like “BeginTrans”,
    “Commit” and “Rollback” respectively, but applied to an object rather than a
    database.


    Managing Business Rules



    In Rockford Lhotka’s original Visual Basic implementation he created a
    BrokenRules class that was made available to every business object in the
    application. In this sample, I have moved this functionality into an abstract
    base class, BusinessObject. I could also have placed an abstract interface to
    the three editing methods here while deferring the implementation to Person,
    but I chose not to for this sample.


    The validity or invalidity of a Person object is determined by maintaining a
    collection of broken business rules. The object is invalid while the collection
    is non-empty. When the count goes to zero the object becomes valid.


    The method that maintains the collection is:


    void RuleBroken(string rule, bool isBroken)


    where:


    rule
    is a description of the business rule. This can be as simple as just the name
    of the property, e.g., “Birth Date”


    isBroken
    indicates whether the rule is broken or not.


    When a rule becomes broken it’s added to the collection. If it’s repeatedly
    broken the algorithm just skips adding it. When it becomes unbroken it is
    removed from the collection.


    When a Person object is first constructed all its fields will be empty, so all
    its rules will be broken. Remember, the rules are:


  • Social Security Number contains exactly 11 alphanumeric characters.

  • Name must be non-empty.

  • Birth Date must be in a valid format.



    In the Person constructor we write:


    RuleBroken(“Social Security Number”, true);

    RuleBroken(“Name”, true);

    RuleBroken(“Birth Date”, true);


    Of course, this is not normally how we should construct objects. Normally
    objects should always be constructed in a valid state. However, the object
    being discussed here is a special kind. And when anything important is being
    done to it, such as persisting it or restoring it, the method ensures that the
    object is valid before it’s persisted or after it’s restored.


    Making a Person Valid



    The idea is that a Person should move from an invalid to a valid state as the
    user types in their data. Initially each item that is the subject of a business
    rule will be incorrect. Then one by one, as the user completes a data entry
    field, each should become valid until Person becomes valid.


    This is achieved by appropriately constructed Person set_xxx properties and
    Form TextChanged events.


    So, for social security number we have:


    in Person.set_SocialSecurityNumber
    property. (simplified for illustration)



    set
    {
    socialSecurityNumber = value;
    RuleBroken(“Social Security Number”,
    socialSecurityNumber.Length != 11);
    }


    in Form.txtSocialSecurityNumber_TextChanged
    event.


    person.SocialSecurityNumber = txtSocialSecurityNumber.Text;


    The TextChanged event occurs on each keystroke and for each keystroke, the
    Person set_SocialSecurityNumber property is called which calls RuleBroken.


    Note the expression: socialSecurityNumber.Length != 11
    .


    This describes as true the condition that makes the rule broken
    .


    On the first keystroke social security number is not 11 characters long so it
    is added to the broken rules collection. On subsequent keystrokes it remains
    broken and so RuleBroken will skip adding it to the collection.
    But when the 11th character is typed RuleBroken
    receives a false value for its argument, indicating that the rule is no longer
    broken, so it is removed from the collection.


    This procedure is repeated for the name and birth date properties until the
    Person object becomes valid.


    Informing a Client that a Person is Valid or Invalid



    The Person class exposes an IsValid property but also exposes Valid and Invalid
    custom events. These two events are handled by the client, in this case a
    Windows Forms application, to enable or disable saving of a Person object. This
    means enabling or disabling the OK and Apply buttons.


    To fire the events the Person class (actually the base class) declares an event
    handler delegate and two events.


    public delegate void EventHandler(object sender, EventArgs e);

    public event EventHandler OnInvalid;

    public event EventHandler OnValid;


    To fire the OnValid
    event



    if (OnValid != null)
    {
    OnValid (this, EventArgs.Empty);
    }



    RuleBroken
    executes this code when the count of broken business rules falls to zero.


    The client handles the event as follows:



    // Subscribe to Person event
    person.OnValid += new Person.EventHandler(Person_OnValid);

    private void Person_OnValid( object sender,
    System.EventArgs e)
    {
    btnOK.Enabled = true;
    btnApply.Enabled = true;
    }



    This event handler code is simplified compared to the actual implementation.
    But it illustrates the general idea.


    The OnInvalid event is implemented similarly.


    The Age Property



    The age property is interesting. It is exposed as read-only so the client
    cannot set it. Instead, it is computed from the birth date. However, it can be
    exposed to the user interface as a dynamic property that is updated each time
    the user enters a different birth date.


    (Note: The sample application expects a date in UK format – day/month/year
    (dd/mm/yyyy).)


    Suppose the user enters 11/1/1969 for date of birth. This gives an age of 33.
    The idea is to have 33 displayed to them in a read-only field the instant they
    type the 9 for 1969. If they make a mistake and decide to change, say, the year
    to 1959 the age field should immediately update itself to show 43. This
    behaviour can be achieved by again defining an event in the Person class:


    public event EventHandler OnNewAge;


    In the Person object, once the birth date is valid this event is raised and it
    is raised again on each subsequent occasion that it becomes valid, if the new
    age differs from the previous valid age.


    The client handles the event as follows:


    private void Person_OnNewAge( object sender,
    System.EventArgs e)
    {
    // Update the displayed age
    lblAge.Text = person.Age.ToString();
    }


    Persistence



    Persistence is implemented using a “manager” object, PersonManager. This has Load, Save and Delete methods and communicates with the Person object. The data access is via ADO.NET and a Microsoft Access database.


    The interface between the two is very simplistic. In a production environment we would need to use .Net remoting and serialization, as the PersonManager class would typically reside on a server.


    Also, later in his book, Lhotka streamlines his concepts and refers to Person and PersonPersist objects to refer to the UI-centric and data-centric halves of the business object.


    Examples



    As indicated, the sample employs a Windows Forms user interface. It is not
    intended to win any prizes for user interface design. Its purpose is to
    illustrate the principal concepts discussed above.


    Editing a new Person


    The screenshot below shows an incompletely edited Person. The birth year is in
    two-digit format but it needs to be four digits, so the OK and Apply buttons
    are disabled.

    Sample Image


    Once the mistake is corrected the Person object becomes valid and the OK and
    Apply buttons are enabled. Note also that the age field is now filled in.


    Sample Image


    Loading a new Person


    A Person is loaded using their social security number. The screenshot below
    shows an incomplete social security number. It needs to be 11 digits long, so
    the Load button is disabled. The name and birth date fields are also disabled,
    as we’re not in edit mode.


    Sample Image


    Once the 11th digit is added the Load button is enabled.


    Sample Image


    Once the Person is loaded the social security number field itself becomes
    disabled.


    Sample Image


    But if we now uncheck the check box to enable editing, all fields are enabled
    and we can also delete the Person at this point because it is no longer new.


    Sample Image


    An ASP.NET Client



    I have not included the code but, for illustration, the screenshots below show
    an ASP.NET Web Forms implementation. This demonstrates a “batch”-style user
    interface. Validation does not take place until all the fields have been
    entered and the user clicks one of the Save buttons. Instead of validation
    being done in the TextChanged events, the ASP.NET required field and custom
    validators are used. These are invoked when a Submit button, i.e., one of the
    Save buttons, is clicked. The first screenshot shows an invalid date with an
    error message. This is corrected in the second screenshot.


    The UI was a bit trickier to implement due to the statelessness of a web
    application. For example, after a submit command the form is reloaded and this
    invalidates the current state; the Loading checkbox must have AutoPostback=true
    and so on. However, the essential point is that no changes are required to the
    Person object.

    Sample Image

    Sample Image

    Downloads



    Download source – 22 Kb (after downloading please consult ReadMe.txt)

  • More by Author

    Get the Free Newsletter!

    Subscribe to Developer Insider for top news, trends & analysis

    Must Read