Press "Enter" to skip to content

Self Aware Objects – Using Custom Attributes To Validate Properties

I’ve done some more work on my Self Aware Objects (SAO) project.

Besides adding some new attributes, I started up a test project, to see how these objects will work in the real world.  For right now, the test project deals with entering customer information into (eventually) some sort of database.

As a quick reminder, the idea behind the SAO project is to eliminate the need for many of the manual unit tests by creating (hopefully) a very stable method to ensure things can’t be done wrong.

One thing I don’t like about the standard use of Model-View-Controller is that the controller is often serving two purposes – input validation and actual processing.  Ideally, I want to connect the input validation directly to the class – where it seems more obvious.

Here’s the definition of the Customer class.

using System;

using SAO.Attributes.Class;
using SAO.Attributes.Property;

namespace Engine.Model
    public class Customer : SAO.SAObject
        public Guid ID { get; set; }

        [CannotBeEmptyString("Name cannot be empty")]
        public string Name { get; set; }

        [CannotBeEmptyString("Address cannot be empty")]
        [MinimumLength(5, "Address cannot be less than 5 characters long")]
        [MaximumLength(100, "Address cannot be more than 100 characters long")]
        public string Address { get; set; }

        [CannotBeEmptyString("City cannot be empty")]
        [MinimumLength(2, "City cannot be less than 2 characters long")]
        [MaximumLength(100, "City cannot be more than 100 characters long")]
        public string City { get; set; }

        [ExactLengthString(2, "State Code must be two characters")]
        [ContainsOnlyLettersAttribute("State Code can only contain letters")]
        public string StateCode { get; set; }

        [MatchesRegEx(@"^(\d{5}-\d{4}|\d{5})$", "ZIP Code must be formatted like '99999' or '99999-9999'")]
        public string ZIPCode { get; set; }

        public Customer()
            ID = Guid.NewGuid();

For right now, don’t worry about the TableName attribute on the class, or the UniqueIdentifier attribute for the ID property.  Those should both come into use when I start working on persisting the data (Yep, I’ve got an idea for that too).

For the other properties, you can see how I declare the valid values for them with the custom attributes like CannotBeEmptyString, ExactLengthString, and MatchesRegEx.

Next, you can see the code I’m using in the code-behind page for the Windows form.

using System.Windows.Forms;

using Engine.Model;

using SAO;

namespace Workbench
    public partial class CustomerEditor : Form
        private Customer _customer { get; set; }

        public CustomerEditor(Customer customer)

            _customer = customer;

            txtName.BindTextPropertyTo(_customer, "Name");
            txtAddress.BindTextPropertyTo(_customer, "Address");
            txtCity.BindTextPropertyTo(_customer, "City");
            txtStateCode.BindTextPropertyTo(_customer, "StateCode");
            txtZIPCode.BindTextPropertyTo(_customer, "ZIPCode");

        private void btnSave_Click(object sender, System.EventArgs e)
            // The SAOValidate method will check the business object against all constraints, 
            // based on the SAO attributes that were set up in the business class definition

                // TODO: Save customer record


I pass in a Customer object to the form, save it to a private variable, and bind its properties to the textbox controls.

The OnClick event for the Save button calls a validation routine that checks all properties values against their custom attributes.  If the object doesn’t pass validation, the IsValid property is set to false, and there is a collection of error messages (the ones defined in the custom attributes of the class).

I eventually want to ties things together a little tighter, like using the regular expression for the ZIP code with the txtZIPCode text box.

Here’s a screenshot of what happens when I try entering in a customer, hitting some different validation failure conditions.

SelfAwareObjects Test 001

That’s what I’ve got so far.

There’s still a lot to experiment with.  I eventually want to have things connect all the way from the UI to the persistence, and have most of it driven by solid ‘black-box’ routines that developers (myself included) won’t need to worry about.

Then we can focus our efforts on the truly difficult parts of development, and not spend so much time on the repetitive and mundane parts.


  1. Jason Cole
    Jason Cole August 27, 2012

    I like this.
    Where we’ve done things similar to your list of bindings (lines 19-23) we’ve done them as little lambda expressions, to give us compile-time catching of typos and refactor-tool friendly code. Thus:

    txtName.BindTextPropertyTo( c => c.Name );
    txtAddress.BindTextPropertyTo( c => c.Address );
    txtCity.BindTextPropertyTo( c => c.City );
    txtStateCode.BindTextPropertyTo( c => c.StateCode );
    txtZIPCode.BindTextPropertyTo( c => c.ZIPCode );

    …you need to do a little more propellor-beanie work to disassemble the lambda than to read the string, but we like the tradeoff.

    • Scott
      Scott August 27, 2012

      That’s nice. I’m going to see if I can replicate that binding method.

      I was doing some work on the performance of the validation routines. Since it uses reflection, it isn’t the fastest thing around. However, it should be fast enough for most applications (the bottleneck is rarely where people think it is). Plus, I believe it may add enough to the ease of development and maintenance to be worth the trade-off. A post for that should be up later today or tomorrow.

Leave a Reply

Your email address will not be published. Required fields are marked *