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

Declarative Data Load for Object Properties & .NET UI Controls

0.00/5 (No votes)
15 May 2010 1  
This article details a new practice to prepare the .NET Business Objects using the data retrieved from the Database and binding them to .NET UI Controls dynamically using Reflection through centralized mapping between a types’ Properties Vs Data-Columns Vs UI-Controls.

Introduction

The conventional practice to bind the Business Objects to the ASP.NET UI controls is to retrieve the content through the database packages/stored procedures and preparing the datasets/Array lists in the Data Layer and sending the Business Object to the UI. UI would bind the Object data to every control through the data bind statements explicitly written in the presentation code.

Background

In the Data Access Layer:

DataAccess yourDAC = new DataAccess();
DataSet dsYourDetails = yourDAC.getDetails(sYourCriteria);
return dsYourDetails; //To the Business Layer

In the Business Layer:

public class YourBO
{
    public String FirstName { get; set; }
    public String LastName { get; set; }
}

We obtain the DataSet from the Data Layer to the Business Layer in a method:

YourBO businessObj = PrepareBusinessObject(dsYourDetails);
return businessObj;//To the Presentation Layer        

private YourBO PrepareBusinessObject(DataSet ds)
{
    YourBO obj = new YourBO ();
    obj.ImpactedChannels = ds.Tables[0].Rows[0]["FirstName"].ToString();
    //any other properties......

    return obj;
}

In the Presentation Layer (ASP.NET Page):

txtFirstName.Text = businessObj.FirstName;
txtLastName.Text = businessObj.LastName;
ddlCities.Text = businessObj.City;

//any other controls……….

In the above implementation, everything is fine in the procedure. Only point to observe here is, during the preparation of the business object we have written the DataColumn name and we are explicitly setting the corresponding property to it. When there is a change in the DataColumn for a property, then if the same property is used at multiple places then everywhere we need to go and change the property and Data column mapping. This would take some significant effort. Similar is the case when we reach the presentation layer. When the page controls increase and if the scenario is the properties and controls and the data columns relationship are used at multiple pages and multiple layers, then we need an implementation where the effort required is less, centralized and easily manageable for any new developer.

Using the Code

Let us go again to the above conventional implementation given as an example. For this case, the proposed solution would be, again no change in the Data Access Layer:

In the Data Access Layer:

DataAccess yourDAC = new DataAccess();
DataSet dsYourDetails = yourDAC.getDetails(sYourCriteria);
return dsYourDetails; //To the Business Layer

In the Business Layer:

public class YourBO
{
    [UI(UIField = "txtFirstName", 
    DataField = "FirstName", TableNo = 0, RowNo = 0)]
    public String FirstName { get; set; }

    [UI(UIField = "txtLastName", 
    DataField = "LastName", TableNo = 0, RowNo = 0)]
    public String LastName { get; set; }
}

Don’t confuse with the declarative attribute “UI” used. We will come back to it in the following section. And now we prepare the Business Object by calling the below method:

private void SetObjectContent(object typeObject, Type objType, DataSet dsObject)
{
    UIAttribute customAttr = null;
    PropertyInfo[] properties = objType.GetProperties();                
    foreach (PropertyInfo property in properties)
        {
            customAttr = (UIAttribute)Attribute.GetCustomAttribute
            		(property, typeof(UIAttribute), false);
            if (customAttr == null)
                continue;
            else
            {
                property.SetValue(typeObject,
                                Convert.ToString(dsObject.
                                Get(customAttr.TableNo, customAttr.RowNo, 
                                customAttr.DataField)), null);
            }
        }            
}

We need to call the above method at appropriate places in our Business Object after obtaining the dataset from the data access layer. E.g.

SetObjectContent(yourBOObj, typeof(YourBO), dsDetails); 

Once this method execution completes, your Business Object is prepared. No need to write explicit statements to bind every DataColumn from the DataSet and set it to every property of the business object. A lot of effort has been saved here. And this is not the complete story. In the Presentation Layer, we need to call this method at appropriate places to bind the ASP.NET UI Controls to the business object dynamically.

private void SetControlContent
(object typeObject, Type objType, Control containerControl)
        {
            PropertyInfo[] properties = objType.GetProperties();
            UIAttribute uiAttribute = null;
            foreach (Control control in containerControl.Controls)
            {
                if (control is TextBox)
                {
                    TextBox txt = (TextBox)control;                    
                    uiAttribute = null;
                    foreach (PropertyInfo property in properties)
                    {
                        uiAttribute = ((UIAttribute)Attribute.GetCustomAttribute
					(property, typeof(UIAttribute), false));
                        if (uiAttribute != null && uiAttribute.UIField == txt.ID)
                        {
                            txt.Text = property.GetValue(typeObject, null).ToString();
                            break;
                        }
                    }
                }
                else if (control is DropDownList)
                {
                    DropDownList ddl = (DropDownList)control;
                    uiAttribute = null;
                    foreach (PropertyInfo property in properties)
                    {
                        uiAttribute = ((UIAttribute)Attribute.GetCustomAttribute
					(property, typeof(UIAttribute), false));
                        if (uiAttribute != null && uiAttribute.UIField == ddl.ID)
                        {
                            ddl.Text = property.GetValue(typeObject, null).ToString();
                            break;
                        }
                    }
                }
    }

E.g. At an appropriate part of the page, we can set the Page Control properties in this fashion.

Type objType = yourBOObj.GetType();
containerControl = Page; //this can be a UserControl/Panel/PlaceHolder etc.
typeObject = yourBOOBj;
SetControlContent(typeObject, objType, containerControl);

Once this method execution completes, all the controls in your page will be set with the corresponding property of the Business Object without writing explicit statements. Now, we have achieved this effort saving just by using an attribute to the Business Object properties. This attribute enables to add additional information for a property which can be accessed through Reflection and used efficiently in different layers. This is the core aspect of this implementation. Let's see its structure:

[global::System.AttributeUsage(AttributeTargets.All, Inherited = false, 
	AllowMultiple = true)]
    public sealed class UIAttribute : Attribute
    {
        // See the attribute guidelines at 
        //  http://go.microsoft.com/fwlink/?LinkId=85236

        public UIAttribute()
        {
        }

        // This is a positional argument
        public UIAttribute(string uiField, string dataField, int tableNo, int rowNo)
        {
            this.UIField = uiField;
            this.DataField = dataField;
            this.TableNo = tableNo;
            this.RowNo = rowNo;
        }

        public string UIField { get; set; }
        public string DataField { get; set; }
        public int TableNo { get; set; }
        public int RowNo { get; set; }
    }

Extending the Implementation

We can extend this implementation not only to simple Business Objects, but also to complex types involving Inheritance, Collection Properties. Whatever might be the type definitions, we need to customize the above implementation so as to achieve this dynamic loading of control data and business object data from the data access layer. Below is a sample implementation to illustrate the Inheritance scenario of the Business Objects in Banking Applications. To differentiate a Collection Property (RowNo=1) and a Non-Collection property (RowNo=0) we can use the attribute value “RowNo” or any user defined parameter of the attribute to inform the Business Layer to set proper data in the Business Object (IncidentBO) properties. Let us consider “ParentItemBO” as part of a Collection Property in our Business Object. The properties in the base type “ParentItemBO” will be virtual, whose definition will be with overridden attribute declarations in the derived Channels like “Item1BO, Item2BO, etc.” Similarly every Item will have its Childs. Hence we need a base Child type and the properties in it will be marked as virtual. All the ParentItem specific ChildItems will derive from “ChildItemBO” by overriding the attribute declarations.

public class YourBO
{
    public YourBO()
    {
        this.ItemCollection = new ArrayList();
    }
    public ArrayList ItemCollection { get; set; }
    public String FirstName { get; set; }
    public String LastName { get; set; }
}

/// 
/// RowNo "1" indicates that this type will be part of the collections.
/// 
public class ParentItemBO
{     
    [TAUI(UIField = "", DataField = "dataColumn1", TableNo = 3, RowNo = 1)]
    public virtual string Property1 { get; set; }

    [TAUI(UIField = "", DataField = "dataColumn2", TableNo = 3, RowNo = 1)]
    public virtual String Property2 { get; set; }

    [TAUI(UIField = "", DataField = "dataColumn3", TableNo = 3, RowNo = 1)]
    public virtual String Property3 { get; set; }

    public virtual List<childitembo /> ChildItems { get; set; }
}

public class ChildItemBO : ParentItemBO
{
    [TAUI(UIField = "", DataField = "childDataColumn1", TableNo = 2, RowNo = 1)]
    public virtual String ChildProperty1 { get; set; }

    [TAUI(UIField = "", DataField = "childDataColumn2", TableNo = 2, RowNo = 1)]
    public virtual string ChildProperty2 { get; set; }
}

This is a concrete Item.

public class Item1BO : ParentItemBO
{
    [TAUI(UIField = "txtProperty1", DataField = "dataColumn1", 
		TableNo = 3, RowNo = 1)]
    public override string Property1 { get; set; }

    [TAUI(UIField = "chkProperty1", DataField = "dataColumn2", 
		TableNo = 3, RowNo = 1)]
    public override String Property2 { get; set; }

    [TAUI(UIField = "ddlProperty1", DataField = "dataColumn3", 
		TableNo = 3, RowNo = 1)]
    public override String Property3 { get; set; }
}

Below are the concrete Child items. These form part of the Collection property (ChildItems) marked in the “ParentItemBO” object during your implementation.

public class ChildItem1BO : ChildItemBO
{
    [TAUI(UIField = "txtChildProperty1", 
	DataField = "childDataColumn1", TableNo = 2, RowNo = 1)]
    public override String ChildProperty1 { get; set; }

    [TAUI(UIField = "rdbChildProperty1", 
	DataField = "childDataColumn2", TableNo = 2, RowNo = 1)]
    public override string ChildProperty2 { get; set; }
}
public class ChildItem2BO : ChildItemBO
{
    [TAUI(UIField = "txtChildProperty2", 
	DataField = "childDataColumn1", TableNo = 2, RowNo = 1)]
    public override String ChildProperty1 { get; set; }

    [TAUI(UIField = "rdbChildProperty2", 
	DataField = "childDataColumn2", TableNo = 2, RowNo = 1)]
    public override string ChildProperty2 { get; set; }
}   

Points of Interest

The above implementation can be used in the components where it takes lot of effort in mapping the properties to the UI controls and where minimum code is required. Maintenance of the three layers becomes easier if we have a centralized mapping of UI Controls-Database Columns-Business Object Properties.

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