Click here to Skip to main content
15,888,968 members
Articles / Programming Languages / C#
Article

Class Data Binding using Custom Attributes

Rate me:
Please Sign up or sign in to vote.
3.40/5 (2 votes)
26 Sep 2006CPOL3 min read 46.9K   308   37   4
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:

C#
[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.

C#
// 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.

C#
[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.

C#
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.

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
Alex Robson is a Senior Developer / System Architect with experience in designing and implementing n-tier solutions in .Net.

Visist www.arobson.net to read other articles and view projects under way.

Comments and Discussions

 
GeneralSerializing attribute information Pin
goran.lazarevic4-May-09 21:47
goran.lazarevic4-May-09 21:47 
GeneralSay hello to LINQ Pin
Chris Richner27-Sep-06 5:21
Chris Richner27-Sep-06 5:21 
GeneralPRETTY good yet basic concept Pin
brady gaster27-Sep-06 2:36
brady gaster27-Sep-06 2:36 
There are a lot of things like this out there, you're off to a good start!

Now make it do relationships. Also, find a DBA that will allow for the execution of generated SQL. That tends to be a problem in most enterprises. Thoughts,anyone?

brady gaster
professional geek
http://www.bradygaster.com

GeneralRe: PRETTY good yet basic concept Pin
Duncan Edwards Jones27-Sep-06 3:03
professionalDuncan Edwards Jones27-Sep-06 3:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.