Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Class Data Binding using Custom Attributes

0.00/5 (No votes)
26 Sep 2006 1  
This article is intended to introduce custom attributes and show how they can be used to create a lightweight data access layer.

Introduction

At the end of this article, you should be comfortable using Custom Attributes.
This article focuses on using them in order to create a quick, flexible data binding mechanism.

Prerequisite Experience/Knowledge

At least a beginner's understanding of ADO.NET and Reflection is required to follow this article.

Understanding Attributes

Attributes are used to add additional metadata to any element within a class (including the class itself).
If you've ever customized a class' XML serialization, you've done it using attributes. The syntax for any attribute is:

[<Attribute>(<attribute values>)] <target element> 

The syntax for defining an attribute is to place the following System attribute before the class that will define your custom:

[AttributeUsage(AttributeTargets.<ElementThisCanApplyTo>, 
	AllowMultiple = (true | false)] 

The Attribute itself can target a class, constructor, field, method, property or all (meaning the attribute could be applied to anything, but you need to be careful with this depending on what you'll be using the attribute to indicate). The class definition for an attribute is quite simple. The constructor(s) for the class are what determines the valid signatures which can be used when applying the attribute to an element. Hang in there, this will all become clear when we take a look at how to implement your first custom attribute. The attribute class is essentially a custom data structure which you will use to attach some metadata to a class element for later retrieval. Let's look at two custom attributes which we'll be using for our data binding.

// This custom attribute will be used to 
// relate a data table name to the class definition
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public   class   ClassDataTable : Attribute
{
    private  string   m_strDataTableName   = "";

    public   string   TableName
    {
        get{return m_strDataTableName;}    
        set{m_strDataTableName=value;}
    }
      
    public   ClassDataTable(string strDataTableName)
    {
        m_strDataTableName = strDataTableName;
    }            
}
//This custom attribute will be used to relate a column name to a 
//field (property only)
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public   class   FieldDataColumn : Attribute
{    private   string   m_strFieldColumnName   = "";

    public    string   ColumnName
    {
        get{return m_strFieldColumnName;}
        set{m_strFieldColumnName = value;}
    }

    public   FieldDataColumn(string strFieldColumnName)
    {
        m_strFieldColumnName = strFieldColumnName;
    }

Now that we have our custom attributes, let's apply them to a new class object.

[ClassDataTable("People")]
public   class   Person
{
	private   string      m_strFirstName	= "";
	private   string      m_strLastName	= "";
	private   int         m_intAge		= 0;
	[FieldDataColumn("FirstName")]
	public   string      FirstName  
	{
		get{return m_strFirstName;}
		set{m_strFirstName = value;}
	}

	[FieldDataColumn("LastName")]
	public   string      LastName   
	{
		get{return m_strLastName;}            
		set{m_strLastName = value;}
	}

	[FieldDataColumn("Age")]
	public   int         Age
	{
		get{return m_intAge;}
		set{m_intAge = value;}
	}
}

Reading Custom Attributes

Perhaps you're not excited yet, after all, all we did was attach strings in the metadata to a class and you're not even sure how you're supposed to make any use of it (or to access it). Don't worry, it's going to get interesting.
Let's make a class with some static functionality that will retrieve this metadata we've just attached. I will say in advance that the following class can and should be abstracted into something more generic (and there will be an article on it eventually), but for now, we'll keep things simple and just focus on the task at hand.

If you're not comfortable or even familiar with Reflection, the good news is you're going to be introduced. After you've finished this article though, I suggest you do some serious reading about one of the most powerful features in the .NET Framework.
We need some way to get the metadata we're attaching to classes via our new custom attributes so we'll create a new class called AttributeReader and give it the following methods: GetBoundTable and GetBoundColumn.

public   class   AttributeReader
{
    //Reads the ClassDataTable custom attribute from the object instance's
    //metadata and returns the table name within the ClassDataTable
    public   static   string   GetBoundTable(object objTarget)
    {
        Type             objType         = objTarget.GetType();
        ClassDataTable   objBoundTable   = (ClassDataTable) 
	    objType.GetClassAttributeByType (objTarget, typeof(ClassDataTable))[0];
        return objBoundTable.TableName;
    }

    //Reads the FieldDataColumn custom attribute from the object instance's 
    //metadata and returns the column name within the FieldDataTable
    public   static   string   GetBoundColumn(object objTarget, 
                            string strFieldName)
    {
        Type              objType        = objTarget.GetType();
        PropertyInfo      objField       = objType.GetProperty(strFieldName);
        FieldDataColumn   objBoundColumn = (FieldDataColumn) 
		objField.GetCustomAttributes (typeof(FieldDataColumn), true)[0];

        return objBoundColumn.ColumnName;
    }
}

All Together Now...

Now that we've got the custom attributes defined, a class object with them applied and a way to read them after the fact, here's a short listing which demonstrates how all this gets used. To test all this together, you will need to include the class listings above along with this to make a little console application which should give you a foundation to work with going forward.
I hope this has been a helpful article and will help you in creating better data tiers in the future.
Please keep in mind that the code in this article is intended to show you the principles of data binding using custom attributes and that all of the code should be optimized so that it will be practical and apply to more situations.

public   class   CustomAttributeTesting
{
    public static void Main()
    {
        //First we'll create a data table and fill it with a test row
        DataSet    objSet      = CreateDataSet();

        //Retrieve a Person instance from the binding function
        Person     objPerson = GetObjectFromBinding(objSet);

        //Output the values of the objPerson instance
        Console.WriteLine("First Name: " + objPerson.FirstName +                   
                    "\nLast Name: " + objPerson.LastName + 
                    "\nAge: " + objPerson.Age.ToString());
        Console.WriteLine("Press Any Key To Continue");
        Console.ReadKey();   
    }
   
    public static DataSet CreateDataSet()
    {
        DataSet    objSet   = new DataSet();
        DataTable  objTable = new DataTable("People");
      
        objTable.Columns.Add("FirstName", typeof(string));
        objTable.Columns.Add("LastName", typeof(string));
        objTable.Columns.Add("Age", typeof(int));
        objTable.Rows.Add(new object[] {"Alex", "Robson", 27});
      
        objSet.Tables.Add(objTable);
        return objSet;
    }

    //This function performs the binding. It only works against the first
    //row in the table.
    public static Person GetObjectFromBinding(DataSet objSet)
    {
        Person        objPerson    = new Person();
        string        strTableName = AttributeReader.GetBoundTable(objPerson);
        DataTable     objTable     = objSet.Tables[strTableName];
        DataRow       objRow       = objTable.Rows[0];      
        string        strColumnName = "";      
 
        //Although it may seem like overkill for the Person class,
        //when you get into larger classes with more members this can
        //be a compact way to perform the binding.
        foreach (PropertyInfo objField in 
                        typeof(Person).GetProperties())
        {
            strColumnName = AttributeReader.GetBoundColumn (objPerson, objField.Name);
            objField.SetValue(objPerson, Convert.ChangeType (objRow[strColumnName], 
				objField.PropertyType),    null);
        }
        return objPerson;
    }
}

History

  • 26th September, 2006: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here