Creating a sample WinForms application, part three: abstract Xpo business class objects

In part two of this series we learned how to create a data layer which tells Xpo how to connect to our data store (in this case, a Sql Express server located on the same machine). Now we have to actually create some classes which map to our data store; that is, Xpo classes which directly relate to a table in the Sql Server database.

Xpo does offer a visual tool allowing you to generate classes from an existing database schema or visually design a class and its properties & relations. However, we’ll be doing a code-first approach and letting Xpo generate the database schema at runtime.

To begin, I’ve added a new project to the DxContactList solution named DxContactList.BusinessLogic. I like to segregate my Xpo classes to a separate assembly to promote code re-use and just to keep things nicely organized.

Within this project, I’ve added a folder named BaseClasses, in which I will place some abstract base classes which will be extended by the application’s business class objects. Xpo does allow you to use abstract classes as a base for a persistent class, but you will want to decorate it with the [NonPersistent] attribute. This may seem a little counter-intuitive, but this really just tells Xpo to not create a table from the base class–its properties will still be persisted when they are extended in a persistent class.

Let’s take a look at the entire PersonBase class:

/// <summary>
    /// Serves as a base class for objects that represent a person. This class cannot be instantiated.
    /// </summary>
    [NonPersistent]
    abstract public class PersonBase : XPCustomObject
    {

        #region Protected members

        /// <summary>
        /// Person first name
        /// </summary>
        protected string _FirstName;

        /// <summary>
        /// Person last name
        /// </summary>
        protected string _LastName;

        /// <summary>
        /// Person date of birth
        /// </summary>
        protected DateTime _DateOfBirth;

        /// <summary>
        /// Person picture
        /// </summary>
        protected Image _Picture;

        #endregion

        #region Public accessors

        /// <summary>
        /// Gets or sets this Person's first name
        /// </summary>
        [Size(50)]
        public string FirstName
        {
            get { return _FirstName; }
            set { SetPropertyValue<string>("FirstName", ref _FirstName, value); }
        }

        /// <summary>
        /// Gets or sets this Person's last name
        /// </summary>
        [Size(50)]
        public string LastName
        {
            get { return _LastName; }
            set { SetPropertyValue<string>("LastName", ref _LastName, value); }
        }

        /// <summary>
        /// Gets or sets this Person's date of birth
        /// </summary>
        public DateTime DateOfBirth
        {
            get { return _DateOfBirth; }
            set { SetPropertyValue<DateTime>("DateOfBirth", ref _DateOfBirth, value); }
        }

        /// <summary>
        /// Gets or sets a picture of this Person
        /// </summary>
        [ValueConverter(typeof(ImageValueConverter))]
        public Image Picture
        {
            get { return _Picture; }
            set { SetPropertyValue<Image>("Picture", ref _Picture, value); }
        }

        /// <summary>
        /// Gets the full name of this Person
        /// </summary>
        [NonPersistent]
        public string FullName
        {
            get
            {
                return String.Format("{0} {1}",
                    _FirstName,
                    _LastName);
            }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the PersonBase class
        /// </summary>
        /// <param name="session">Current Xpo Session</param>
        public PersonBase(Session session)
            : base(session)
        {

        }

        /// <summary>
        /// Returns a string representation of this Person
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return String.Format("{0} {1}",
                _FirstName,
                _LastName);

        }   //End the ToString() method

    }   //End the PersonBase class

Our persistent classes will inherit from XPCustomObject. Xpo offers a number of base classes that will tell that data layer that a class is to be persisted to the data store. Your requirements will ultimately dictate which of these base classes you derive from, but because we will be letting Sql Server manage the unique identifier for each object (that is, an identity field), we will use the XPCustomObject class for all of our objects.

Because this is an abstract class and will not be mapped directly to a table in the database, we will not have any sort of primary key property in the class.

Note that an XPCustomObject class must either provide a property with the [Key] attribute (stating that it is the primary key/identity field) or the class must be marked as [NonPersistent]. Xpo will throw an exception at runtime if you neglect to do this!

The class itself will have a few simple members with the protected modifier. Xpo does not persist protected/private members; only public properties are persisted. To ensure our members are persisted I’ve created public accessors for each protected member.

The first thing of note are some attributes applied to a few of these properties. The string properties have a [Size] attribute which will limit the size of the field in the database table. By default, Xpo will create each string property as an nvarchar(256) in Sql Server, but that may be more (or less) than what is actually necessary.

You may want to review the table of data types supported by Xpo and how it maps .NET data types to data store types. These default mappings can all be changed by decorating a property with the DbType attribute.

The Picture property is a .NET Image object, but we can’t guarantee that our database system will automatically know how to deal with a binary field. To accommodate this, we decorate the property with the ValueConverter attribute which tells Xpo that we (or Xpo itself) will be performing some sort of value conversion on the property when it is saved to or retrieved from the database. You can create your own value converters to perform this data conversion or use one of the built-in converters offered by Xpo. In this case, we are using Xpo’s built-in ImageValueConverter, which can convert our property between a .NET Image object and a byte array.

The last property in the class is a [NonPersistent] property called “FullName”, which simply returns the name of a person by combining the FirstName and LastName members. NonPersistent properties are useful for simple cases like this, or when we want to have a property calculated from existing persistent properties but not have it persisted in the database.

The only other thing of note in this base class is the constructor, which takes a Session parameter and extends the XPCustomObject base class constructor. Xpo objects require a Session (or UnitOfWork) and you may notice that if you use the DevExpress ORM Persistent Object template in Visual Studio a second constructor is added. This constructor does not require the Session parameter, but because we will always be using a Session in conjunction with our object, there’s no need to keep this constructor around.

We’ve taken a brief look at some of the attributes provided by Xpo as well as discussed some class structure and best-practices. This will be continued in part four, as we build some actual persistent classes.

Creating a sample WinForms application, part three: abstract Xpo business class objects

Creating a sample WinForms application, part one

I thought that perhaps a good way to expose new DevExpress developers to the various products and controls might be to create a small, simple application. Borne from this idea is DxContactList, a basic application designed to be a digital address book. I came up with some simple functionality that help you become more familiar with some different controls & products:

  • XtraEditors as well as creating some custom controls
  • XtraGrid
  • XtraMap
  • XtraReports
  • eXpress Persistent Objects (Xpo), DevExpress’ ORM product that will handle our data-access and mapping.
  • XtraRichEdit control

I’ve already built the application and I hope to present it to you over the next few weeks in a series of posts. We’ll take a look at each portion of the application, deconstruct how I’ve built it and point out some tips and tricks to make your life easier if you’re trying to develop something similar.

I’ll be starting with the back-end & business logic which is chiefly comprised of our persistent classes. You way want to review some of the Xpo documentation to familiarize yourself with it first. Additionally, I find that the Xpo Best Practices knowledge base article is valuable in helping you avoid some common mistakes as you begin to learn.

Creating a sample WinForms application, part one