Bind business objects to ASP. NET Form Controls Using Reflection

Source: Internet
Author: User

Use reflection to bind business objects to ASP. NET Web forms with a single line of code to reduce complexity and errors. This article contains links to English sites. Note that in the example file, the comments of programmers are in English. In this document, the comments are translated into Chinese to facilitate your understanding .)

Introduction

Among the most common tasks of Web developers, one task is to be executed repeatedly: create a simple form for updating database tables. We will create a list page and a form page. The list page displays records in the form of tables. The form page contains the appropriate form controls used for various database fields. Many developers also use business objects that represent database tables to organize code into multi-layer designs. If you use a Business Object (Document) to represent database tables, the code of many forms looks as follows:

<Script runat = "server">
Protected void Page_Load (Object Src, EventArgs E ){
If (! IsPostBack ){
Document document =
Documents. GetDocument (Request. QueryString ["entid"]);

Title. Text = document. Title;
Active. Checked = document. Active;
CreatedDate. Text = document. CreatedDate. ToString ();
AuthorID. FindByValue (document. AuthorID. ToString (). Selected =
True;
//... And so on
HtmlBody. Text = document. HtmlBody;
}
}
Protected void SaveButton_Click (Object Src, EventArgs E ){
Document document =
Documents. GetDocument (Request. QueryString ["entid"]);

Document. Title = Title. Text;
Document. Active = Active. Checked;
Document. CreatedDate = Convert. ToDateTime (CreatedDate. Text );
Document. AuthorID = Convert. ToInt32 (AuthorID. SelectedItem. Value );
//... And so on
Document. HtmlBody = HtmlBody. Text;

Documents. Update (document );
}
</Script>

Simplify and shorten the form code

In the above Code, each control is converted explicitly and set to the correct attribute of the Form Control. Depending on the number of properties and Form Controls, this part of code may become longer and difficult to manage. The code should also contain error correction and ListControl for type conversion, which will further increase complexity. Even if the form is generated by a code generation tool such as Eric J. Smith's excellent CodeSmith, it is easy to introduce errors when any custom logical relationship is required.

With reflection, you can use only one line of code to bind all the attributes of the Business Object to the corresponding form control, reducing the number of lines of code and enhancing readability. After the establishment of the reflection system, the above Code will be simplified:

Protected void Page_Load (Object Src, EventArgs E ){
If (! IsPostBack ){
Document document =
Documents. GetDocument (Request. QueryString ["entid"]);

FormBinding. BindObjectToControls (document );
}
}
Protected void Save_Click (Object Src, EventArgs E ){
Document document =
Documents. GetDocument (Request. QueryString ["entid"]);

FormBinding. BindControlsToObject (document );

Documents. Update (document );
}

This code can be used for all standard ASP. NET controls TextBox, DropDownList, CheckBox, etc.) and many third-party controls such as Free TextBox and Calendar ar Popup ). Regardless of the number of Business Object Attributes and Form Controls, this line of code provides all the functions required, as long as the Form Control ID matches the Business Object attribute name.

Start: retrieve the attribute list from reflection

First, we need to check the properties of the Business Object and find the ASP. NET Control with the same ID as the property name of the business object. The following code forms the foundation for binding search:

Public class FormBinding {
Public static void BindObjectToControls (object obj,
Control container ){
If (obj = null) return;
Type objType = obj. GetType ();
PropertyInfo [] objPropertiesArray =
ObjType. GetProperties ();

Foreach (PropertyInfo objProperty in objPropertiesArray ){

Control control =
Container. FindControl (objProperty. Name );
If (control! = Null ){
// Process controls...
}
}
}
}

In the above Code, the BindObjectsToControls method accepts the Business Object obj and a container control. The container control is usually the Page Object of the current Web form. If the version used is ASP. NET 1.x MasterPages that will change the nesting order of controls at runtime, You need to specify the Content control of the Form Control. In ASP. NET 1.x, The FindControl method processes nested controls and named containers.

In the above Code, we obtain the Type of the business object, and then use this Type to obtain the array of the PropertyInfo object. Each PropertyInfo object contains information about the attributes of the business object and the ability to obtain and set values from the business object. We use the foreach loop to check the container of an ASP. NET Control with the ID attribute corresponding to the Business Object Property Name (PropertyInfo. Name. If a control is found, the property value is bound to the control.

Bind object property values to controls

Most of the operations in the process are performed in this phase. We need to fill in the found control with the property value of the object. One way is to create an if... else statement for each control type. All controls derived from ListControlDropDownList, RadioButtonList, CheckBoxList, and ListBox have public interfaces that can be accessed in a unified manner, so they can be grouped together. If the control we found is ListControl, we can convert it as ListControl and set the selected items:

Control control = container. FindControl (objProperty. Name );
If (control! = Null ){
If (control is ListControl ){
ListControl listControl = (ListControl) control;
String propertyValue = objProperty. GetValue (obj,
Null). ToString ();
ListItem listItem =
ListControl. Items. FindByValue (propertyValue );
If (listItem! = Null) listItem. Selected = true;
} Else {
// Process other control types
}
}

Unfortunately, other control types are not derived from the parent class. The following public controls have. Text string attributes: TextBox, Literal, and Label. However, this property is not derived from the public parent class, so you need to convert each control type separately. We also need to convert other control types, such as the Calendar control, so that the appropriate attribute is the SelectedDate attribute in the Calendar example ). To include all standard ASP. NET Form Controls and access the correct properties of the form controls, there is no need for too many lines of code.

If (control is ListControl ){
ListControl listControl = (ListControl) control;
String propertyValue = objProperty. GetValue (obj,
Null). ToString ();
ListItem listItem = listControl. Items. FindByValue (propertyValue );
If (listItem! = Null) listItem. Selected = true;
} Else if (control is CheckBox ){
If (objProperty. PropertyType = typeof (bool ))
(CheckBox) control). Checked = (bool)
ObjProperty. GetValue (obj, null );
} Else if (control is Calendar ){
If (objProperty. PropertyType = typeof (DateTime ))
(Calendar) control). SelectedDate = (DateTime)
ObjProperty. GetValue (obj, null );
} Else if (control is TextBox ){
(TextBox) control). Text = objProperty. GetValue (obj,
Null). ToString ();
} Else if (control is Literal )(
//... And so on. It can also be used for tags and other attributes.
}

This method fully covers the standard ASP. NET 1.x controls. From this perspective, we have a fully functional BindObjectToControls method. But at the same time, the application scope of this method is limited, because it only takes into account the built-in ASP. NET 1.x control. If you want to support the new ASP. NET 2.0 control, or to use any third-party control, we must reference the control assembly in the FormBinding project and add the control type to if... else list.

The solution to this problem is to use reflection for the second time to view the properties of each control and find out whether the control has the property type corresponding to the property type of the business object.
Set the value of an unknown control with a known property

As mentioned above, some controls share the string property. Text. Most Form Controls use this property in essentially the same way. This attribute is used to obtain and set user input data. A large number of controls also use other public attributes and property types. The following are some of these attributes. the DateTime attribute of SelectedDate, which is used in many calendar and date selector controls. the Boolean attribute of Checked, which is used in a boolean control. value string attribute, which is common in hidden controls. The four attributes string Text, string Value, bool Checked, and DateTime SelectedDate are the most common control attributes. If the system can be designed to bind any control type to these attributes, our binding method applies to any control that uses those four attributes.

In the following code, we use reflection for the second time. This is used for Form Controls, rather than for Business Objects) to determine whether it has any common attributes. If yes, set the property value of the business object to the property of the control. As an example, we will iterate the entire PropertyInfo array and find the string attribute called. Text. If the control has this property, data is sent from the Business Object to the property of the control.

If (control is ListControl ){
//...
} Else {
// Obtain the control type and attributes
//
Type controlType = control. GetType ();
PropertyInfo [] controlPropertiesArray =
ControlType. GetProperties ();

// Search for. Text attributes
//
Foreach (PropertyInfo controlProperty
In controlPropertiesArray ){
If (controlPropertiesArray. Name = "Text "&&
ControlPropertiesArray. PropertyType = typeof (String )){
// Set the. Text attribute of the control
//
ControlProperty. SetValue (control,
(String) objProperty. GetValue (obj, null), null );

}
}

}

If. Text is found, use the GetValue method of the PropertyInfo class to retrieve the value from the properties of the business object. Then, use the SetValue method of the. Text attribute of the control. Here, we also use the Type command to set the property of the control to typeof (String), and use the (String) symbol to explicitly convert the value from the property.

To make the BindObjectToControls method complete, we also need to deal with other public attributes, namely. Checked,. SelectedDate and. Value. In the following code, we package the control property search into a helper method called FindAndSetControlProperty to simplify the code.

If (control is ListControl ){
//...
} Else {
// Obtain control attributes
//
Type controlType = control. GetType ();
PropertyInfo [] controlPropertiesArray =
ControlType. GetProperties ();

Bool success = false;
Success = FindAndSetControlProperty (obj,
ObjProperty, control, controlPropertiesArray,
"Checked", typeof (bool ));

If (! Success)
Success = FindAndSetControlProperty (obj,
ObjProperty, control, controlPropertiesArray,
"SelectedDate", typeof (DateTime ));

If (! Success)
Success = FindAndSetControlProperty (obj,
ObjProperty, control, controlPropertiesArray,
"Value", typeof (String ));

If (! Success)
Success = FindAndSetControlProperty (obj,
ObjProperty, control, controlPropertiesArray,
"Text", typeof (String ));

}

Private static void FindAndSetControlProperty (object obj,
PropertyInfo objProperty, Control control,
PropertyInfo [] controlPropertiesArray, string propertyName,
Type type ){
// Iterate the entire control attribute

Foreach (PropertyInfo controlProperty in
ControlPropertiesArray ){
// Check the matched name and type
If (controlPropertiesArray. Name = "Text "&&
ControlPropertiesArray. PropertyType = typeof (String )){
// Set the property of the control
// Business Object Property Value
ControlProperty. SetValue (control,
Convert. ChangeType (
ObjProperty. GetValue (obj, null), type), null );
Return true;
}
}
Return false;
}

The order of the above attribute checks is very important, because some controls have multiple of the above attributes, but we only want to set one. For example, the CheckBox control has both the. Text and. Checked attributes. In this example, we want to use the. Checked attribute instead of the. Text attribute, so we put. Checked first in the attribute search order. In any case, if a control property with the correct name and type is found, set the property of the control to the value of the business object property.

From this perspective, we have a fully functional BindObjectToControls method. With this method, we can call any combination of classes and controls anywhere on the ASPX form, which is indeed effective. Now, we need to create a method to reverse when submitting the form. We need to retrieve the new value from the control that represents the user input, instead of setting the value of the control property to the value of the business object.

Reversal Process: BindControlsToObject

In the BindControlsToObject method, we will start by retrieving the attribute list from the business object in the same way, and then use the FindControl method to find the control with the ID that matches the object property. If the control is found, the value is retrieved and returned to the business object. This section also contains separate code for ListControl because these controls have public interfaces. We will use another helper method to search for and retrieve the value in the control, and then return the value to the business object.

Public static void BindControlsToObject (object obj,
Control container ){
Type objType = obj. GetType ();
PropertyInfo [] objPropertiesArray = objType. GetProperties ();

Foreach (PropertyInfo objProperty in objPropertiesArray ){

If (control is ListControl ){
ListControl listControl = (ListControl) control;
If (listControl. SelectedItem! = Null)
ObjProperty. SetValue (obj,
Convert. ChangeType (list. SelectedItem. Value,
ObjProperty. PropertyType), null );

} Else {
// Obtain control attributes
//
Type controlType = control. GetType ();
PropertyInfo [] controlPropertiesArray =
ControlType. GetProperties ();

Bool success = false;
Success = FindAndGetControlProperty (obj,
ObjProperty, control, controlPropertiesArray,
"Checked", typeof (bool ));

If (! Success)
Success = FindAndGetControlProperty (obj,
ObjProperty, control, controlPropertiesArray,
"SelectedDate", typeof (DateTime ));

If (! Success)
Success = FindAndGetControlProperty (obj,
ObjProperty, control, controlPropertiesArray,
"Value", typeof (String ));

If (! Success)
Success = FindAndGetControlProperty (obj,
ObjProperty, control, controlPropertiesArray,
"Text", typeof (String ));

}
}
}

Private static void FindAndGetControlProperty (object obj,
PropertyInfo objProperty, Control control, PropertyInfo []
ControlPropertiesArray, string propertyName, Type type ){
// Iterate the entire control attribute
Foreach (PropertyInfo controlProperty in
ControlPropertiesArray ){
// Check the matched name and type
If (controlPropertiesArray. Name = "Text "&&
ControlPropertiesArray. PropertyType = typeof (String )){
// Set the property of the control
// Business Object Property Value
Try {
ObjProperty. SetValue (obj,
Convert. ChangeType (
ControlProperty. GetValue (control, null ),
ObjProperty. PropertyType), null );
Return true;
} Catch {
// The widget from the form cannot be
// Convert the data
// ObjProperty. PropertyType
Return false;
}
}
}
Return true;
}

After the two methods are completed, the form syntax is simplified, as described in the preceding simplified and simplified form code. The type conversion and Error Correction of each property and control are automatically performed. These two methods BindObjectToControls and BindControlsToObject provide great flexibility for developers to create forms. They can also be used to handle the following common solutions:

• If you add a new property to a business object and want to access the new property on the form, developers only need to add the control to the page, and set the Control ID as the name of the new property. The FormBinding method will process everything else.

• If a developer needs to change the type of the control used for a specific attribute, for example, from TextBox to a third-party HTML editor control, he/she only needs to ensure that the new control has one of the preceding attributes, for example. text), the form will work in exactly the same way as before.

• You can use the TextBox Control to quickly generate a form, but the input will still be converted to the correct type applicable to the business object attributes. For example, you can use the TextBox Control to replace the Calendar control or a third-party date selector control. If you enter a DateTime string as the value, the value in the. Text attribute of TextBox is converted to DateTime, just as it is the SelectedDate attribute on the calendar control. If you change TextBox to the date selector control later, the logical relationship remains unchanged.

• By changing all controls to Literal controls, developers can quickly create "views" pages. The. Text attribute of Literal will be set as the value of the business object property, just as it is a TextBox.

• In the actual solution, the form also contains other data types and custom configurations. Code used to process these specific operations can be placed after calls to BindObjectToControls and BindControlsToObject.


Expansion of performance and FormBinding Solutions

Some developers may want to know whether the performance degradation caused by reflection is worthwhile. In my tests, I used objects with seven attributes: int categorentid, bool Active, DateTime Created, int CategoryID, String Title, string Author, and String htmlText, bindObjectToControls takes about 1/3 milliseconds, and BindControlsToObject takes about 1 millisecond. These values are obtained by running the BindObjectToControls and BindControlsToObject Methods 1000 times in a loop. For common "add" and "edit" forms solutions, such performance should not cause any major problems, and it can indeed improve the development speed and flexibility.

Although this method applies to almost every form, you may need to modify the above Code. In some schemes, the control to be used by developers may not use one of the above attributes as its main interface. In this case, you need to update the FormBinding method to include this property and type.

Conclusion

The two FormBinding Methods BindObjectToControls and BindControlsToObject can be used to greatly simplify the form code and provide maximum flexibility for ASP. NET form development. I have benefited a lot from their use and hope your team can also benefit from them.

References 

• ASP. NET Unleashed
• Programming Microsoft ASP. NET


Author Profile

John Dyer is a top Web Developer at Dallas Theological Seminary responsible for guiding their prestigious Online Training Program, which is built on the Community Server of Telligent System.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.