One of the purposes of creating code-behind files is to use them as business components, independing processing routines that are sharable among different pages. In this way, a generally useful processing routine does not have to be duplicated on all pages that need it; any page can inherit its class definition and take advantage of its processing. Therefore, the code-behind file needs to be independent of any page that uses it.
Component Sharing through Naming Conventions
One of the ways in which a code-behind file can be shared among multiple pages is to use common names for common controls. Imagine, for instance, that two ASP.NET pages require the same recordset from BooksDB.mdb; each page displays the recordset in a GridView. Rather than having to create two separate code-behind files for each of these pages, a single code-behind file can be used, assuming that the two GridViews have the same id. This is a necessary requirement since the code-behind recordset is bound to a particular id value.
The following code-behind file can be used as a general-purpose recordset retrieval function for the BooksDB.mdb Books table.
Imports System.Data.OleDb Public Class DBClass Inherits Page Protected WithEvents DisplayGrid As GridView Sub Get_Data Dim DBConnection As OleDbConnection Dim DBCommand As OleDbCommand Dim DBReader As OleDbDataReader Dim SQLString As String DBConnection = New OleDbConnection( _ "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & Server.MapPath("../Databases/BooksDB.mdb")) DBConnection.Open() SQLString = "SELECT * FROM Books ORDER BY BookID" DBCommand = New OleDbCommand(SQLString, DBConnection) DBReader = DBCommand.ExecuteReader() DisplayGrid.DataSource = DBReader DisplayGrid.DataBind() DBReader.Close() DBConnection.Close() End Sub End Class
The Get_Data subprogram retrieves all fields from all records in the table. Notice that the retrieved recordset is bound to a GridView with id="DisplayGrid".
Just because the full set of database fields have been retrieved does not mean that particular pages have to use them all. By using different <asp:BoundField> controls for their GridViews, different pages can display different outputs. The following code represent two ASP.NET pages, each of which imports the above code-behind class but which use the retrieved recordset for different display purposes.
Both pages use identical page directives to import the DBClass.vb file. Both pages include a Page_Load script to call subprogram Get_Data when they load. Both pages have identical id="DisplayGrid" GridViews. The pages differ, however, in how they employ the retrieved recordset by the differing data columns that are bound to their GridViews.
Passing SQL Strings
It is not necessary that different pages be limited by the single recordset retrieved by the code-behind page. An ASP.NET page can, in fact, pass an SQL statement to the code-behind page and have a personalized recordset returned. A minor rewrite of the code-behind page is necessary.
Imports System.Data.OleDb Public Class DBClass Inherits Page Protected WithEvents DisplayGrid As GridView Sub Get_Data (SQLString As String) Dim DBConnection As OleDbConnection Dim DBCommand As OleDbCommand Dim DBReader As OleDbDataReader DBConnection = New OleDbConnection( _ "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & Server.MapPath("../Databases/BooksDB.mdb")) DBConnection.Open() DBCommand = New OleDbCommand(SQLString, DBConnection) DBReader = DBCommand.ExecuteReader() DisplayGrid.DataSource = DBReader DisplayGrid.DataBind() DBReader.Close() DBConnection.Close() End Sub End Class
Here, the signature of the subprogram includes an SQLString argument through which an SQL statement is passed. Then that statement is issued through the Command object to retrieve that particular selection of fields. Different subsets of records can be retrieved and even different database tables can be accessed.
Passing Control Information
In previous examples, it is assumed that all controls to which the recordset is bound are GridViews with id="DisplayGrid". But this doesn't have to be the case. Any control can identify itself to the code-behind file and receive the recordset it needs. In the following example, an <asp:Repeater> control calls a code-behind subprogram and retrieves its personalized recordset. Again, only a slight change is needed in the code-behind file to make this generalization.
Imports System.Data.OleDb Public Class DBClass Inherits Page Sub Get_Data (SQLString As String, ControlID As String) Dim DBConnection As OleDbConnection Dim DBCommand As OleDbCommand Dim DBReader As OleDbDataReader Dim TheControl As Object = FindControl(ControlID) DBConnection = New OleDbConnection( _ "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & Server.MapPath("../Databases/BooksDB.mdb")) DBConnection.Open() DBCommand = New OleDbCommand(SQLString, DBConnection) DBReader = DBCommand.ExecuteReader() TheControl.DataSource = DBReader TheControl.DataBind() DBReader.Close() DBConnection.Close() End Sub End Class
The subprogram is passed the id of a control as argument ControlID along with an SQL statement. The subprogram uses the passed id to find that control on the page and assign it to variable TheControl. This variable is not explicitly typed since it is not known in advance what kind of control it is. TheControl is the object to which the retrieved recordset is bound, using the passed SQL statement to retrieve particular records and particular fields from particular tables.
The page that imports this code-behind page is shown below with a call to subprogram Get_Data, passing along an SQL statement and identification of the Repeater to which the returned recordset is bound.
Now the code-behind file serves a general-purpose data retrieval function that can be called by any page to supply a recordset for any of its controls. Of course, the control must use compatible binding techniques. For instance, a DropDownList has its DataTextField and DataValueField identified for binding. The present code-behind file does not answer this need. Still, it serves the needs of all information display controls.
Returning Recordsets from Functions
A more generally useful technique for supplying database information to a page, one that does not involve providing control information to the code-behind class, is to convert the code-behind subprogram to a function which returns a DataReader. By employing this technique, a code-behind file can become totally independent of any Web pages which inherit it.
In the following example, a GridView is used to display a subset of information from the BooksDB.mdb database. However, the code-behind class needs no knowledge of the page to which it returns its results.
The On_Load script issues a function call to Return_Reader() in the code-behind file, and binds the returned recordset to the GridView. The DBClass.vb code-behind file is shown below. Function Return_Reader() returns the full set of records drawn from the Books table of the BooksDB.mdb database. That is, it returns a DataReader (the DBReader recordset) to any page that calls the function.
Imports System.Data.OleDb Public Class DBClass Inherits Page Function Return_Reader() Dim DBConnection As OleDbConnection Dim DBCommand As OleDbCommand Dim DBReader As OleDbDataReader Dim SQLString As String DBConnection = New OleDbConnection( _ "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & Server.MapPath("../Databases/BooksDB.mdb")) DBConnection.Open() SQLString = "SELECT * FROM Books ORDER BY BookID" DBCommand = New OleDbCommand(SQLString, DBConnection) DBReader = DBCommand.ExecuteReader() Return DBReader DBReader.Close() DBConnection.Close() End Function End Class
Notice that even though the code-behind class returns a full recordset, the GridView uses BoundFields to display only selected fields from the recordset. Thus, any number of different pages can import this class and call its function to display their own selected fields of information. The class is a general-purpose database access function that can be adapted to numerous uses.