5,276,156 members and growing! (20,254 online)
Email Password   helpLost your password?
Languages » C# » General     Intermediate

Class Data Binding using Custom Attributes

By Alex S. Robson

This article is intended to introduce Custom Attributes and show how they can be used to in creating a light weight data access layer.
C#, Windows, .NET, Visual Studio, Dev

Posted: 26 Sep 2006
Updated: 26 Sep 2006
Views: 12,501
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
2 votes for this Article.
Popularity: 1.02 Rating: 3.40 out of 5
0 votes, 0.0%
1
0 votes, 0.0%
2
1 vote, 50.0%
3
0 votes, 0.0%
4
1 vote, 50.0%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article
Source Language C#
Target Framework .Net 2.0
Technologies Used ADO.Net, Reflection
Prequisite Experience/Knowledge At least a beginner's understanding of ADO.Net and Reflection is required to follow this article.

Introduction

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

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 classes Xml serialization, you've done it using attributes. The syntax for any attribute is:

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

The syntax for defining a 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 this all 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();

        //Retreive 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;
    }
}

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

About the Author

Alex S. Robson


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.
Occupation: Web Developer
Location: United States United States

Other popular C# articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 3 of 3 (Total in Forum: 3) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralSay hello to LINQmemberChris Richner6:21 27 Sep '06  
GeneralPRETTY good yet basic conceptmemberbrady gaster3:36 27 Sep '06  
GeneralRe: PRETTY good yet basic conceptmemberDuncan Edwards Jones4:03 27 Sep '06  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 26 Sep 2006
Editor:
Copyright 2006 by Alex S. Robson
Everything else Copyright © CodeProject, 1999-2008
Web10 | Advertise on the Code Project