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;
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;
private YourBO PrepareBusinessObject(DataSet ds)
{
YourBO obj = new YourBO ();
obj.ImpactedChannels = ds.Tables[0].Rows[0]["FirstName"].ToString();
return obj;
}
In the Presentation Layer (ASP.NET Page):
txtFirstName.Text = businessObj.FirstName;
txtLastName.Text = businessObj.LastName;
ddlCities.Text = businessObj.City;
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;
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; 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
{
public UIAttribute()
{
}
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 Child
s. 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; }
}
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.