Code Partitioning

ASP.NET offers better solutions for code partitioning than were offered with traditional ASP. In traditional ASP, your primary options were to put your partitioned code into include files, separate ASP files that you executed with Server.Execute or business object components. The extra overhead of having multiple copies of include files in memory (one for each file that uses it) made this solution undesirable. Using Server.Execute was somewhat helpful, but you could not pass the executed ASP file any additional query string parameters. Business object components were the only good alternative in traditional ASP. With ASP.NET, you can use include files (not recommended) and business object components, as well as two new techniques to partition code: code-behind classes and user controls.

Code-Behind Classes

You can use a code-behind class as an effective way of separating client-side HTML code from server-side event-processing code. Breaking up the code in this way makes it easier to track down problems because you don’t have to sift through user-interface code to get to the business logic code. Also, if the user-interface code of your project is created by a different group of developers than your business logic code is, using code-behind classes prevents one group from accidentally stepping on the code of the other group. Listings 4.1, 4.2, and 4.3 demonstrate how easy code-behind classes are to implement.

Notes About the Code Partitioning Example

The example in the “Code Partitioning” section of this chapter builds upon itself and uses the pubs database that comes standard with SQL Server 2000.


Listing 4.1. User-Interface Code for an Author Search
<%@ Language="C#" Inherits="AuthorSearch" Src="listing42.cs" %> 
<form id="frm_search" method="post" runat="server"> 
Search for an author:<br> 
Name: 
<asp:TextBox id="txt_name" runat="server" /> 
<asp:Button id="btn_name" text="Search" onClick="btn_name_click" 
runat="server" /> 
<br> 
<asp:Label id="lbl_author" name="lbl_author" runat="server" /> 
</form> 

Listing 4.2. Code-Behind Class to Handle User Input
using System; 
using System.Web.UI; 
using System.Web.UI.WebControls; 

public class AuthorSearch : Page 
{
      public Label lbl_author; 
      public TextBox txt_name; 
      public void btn_name_click(Object sender, EventArgs e) 
      {
             lbl_author. Text = "You are searching for " 
                 + txt_name. Text; 
      } 
} 

Listing 4.3. User-Interface Code for an Author Search
Imports System 
Imports System.Web.UI 
Imports System.Web.UI.WebControls 

Public Class AuthorSearch 
Inherits Page 
      Public lbl_author As Label 
      Public txt_name As TextBox 

      Public Sub btn_name_click(sender As Object, e As EventArgs) 
           lbl_author. Text = "You are searching for " & _ 
               txt_name. Text 
      End Sub 
End Class 

You can see that having all your user-interface code in one place and all your business logic code in another makes it much easier to comprehend and maintain. User controls, described next, add another dimension to code partitioning: reusability.

User Controls

Have you ever found a problem with a piece of your code and gotten completely flustered because, after you fixed it, you had to make the change in a hundred other places where you used the exact same code? In traditional ASP, common business logic functions were often encapsulated in include files or in components. Elements of the user-interface, however, were encapsulated much more rarely. This was typically the case because there was no convenient way to do it. ASP.NET introduces user controls to help ease the user-interface encapsulation nightmare and enable you to fix bugs once, having them seamlessly applied everywhere the code is used. This reusability greatly reduces the time it takes to test, maintain, and implement web applications.

Continuing with the previous example, suppose that you want to search for authors by state as well as by name. The code to create a drop-down box of state names is an ideal candidate for a user control as can be seen in Listing 4.4.

Listing 4.4. Author Search User-Interface Code Implementing a User Control
<%@ Language="C#" Inherits="AuthorSearch" Src="listing48.cs" %> 
<%@ Register TagPrefix="ch04" TagName="StateList" src="listing45.ascx" %> 

<form id="frm_search" method="post" 
     runat="server"> 
Search for an author:<br> 
Name: 
<asp:TextBox id="txt_name" runat="server" /> 
<asp:Button id="btn_name" text="Search" onClick="btn_name_click" 
     runat="server" /> 
<br>OR<br> 
<ch04:StateList id="uc_statelist" runat="server" /> 
<asp:Button id="btn_state" text="Search" onClick="btn_state_click" 
     runat="server" /> 
<br> 
<asp:Label id="lbl_author" name="lbl_author" runat="server" /> 
</form> 

At the top of Listing 4.4, you register a user control that contains a DropDownList server control populated with all the state abbreviations. Any page that needs to access a list of all the states can use the same control. Listings 4.5, 4.6, and 4.7 present the code for the user control and its code-behind class.

Listing 4.5. User Control Code
<%@ Language="C#" Inherits="Chapter4.StateList" %> 

State: 
<asp:DropDownList id="lst_state" runat="server"> 
     <asp:ListItem>AL</asp:ListItem> 
     <asp:ListItem>AK</asp:ListItem> 
     <asp:ListItem>WV</asp:ListItem> 
     <!—Remaining states omitted for space reasons—> 
     <asp:ListItem>WI</asp:ListItem> 
     <asp:ListItem>WY</asp:ListItem> 
     <asp:ListItem>DC</asp:ListItem> 
</asp:DropDownList> 

Listing 4.6. Code-Behind Class for User Control
using System; 
using System.Web.UI; 
using System.Web.UI.WebControls; 

namespace Chapter4 
{
      public class StateList : UserControl 
      {
            protected DropDownList lst_state; 

            public string SelectedState 
            {
                  get {return lst_state.SelectedItem. ToString();} 
            } 
      } 
} 

Listing 4.7. Code-Behind Class for User Control (Visual Basic .NET)
Imports System 
Imports System.Web.UI 
Imports System.Web.UI.WebControls 

Namespace Chapter4 
     Public Class StateList 
           Inherits UserControl 

           Protected lst_state As DropDownList 

           Public ReadOnly Property SelectedState() As String 
                Get 
                       Return lst_state.SelectedItem. ToString() 
                End Get 
           End Property 
     End Class 
End Namespace 

To be able to reference your user control as its own Type (such as StateList), you must create a compiled assembly.You would use the following compile script to compile the C# version of the code in Listing 4.6:

csc /t:library /out:chapter4.dll /r:System.Web.dll listing46.cs 

The Visual Basic code in Listing 4.7 can be compiled using the following compile script:

vbc /t:library /out:chapter4.dll /r:System.Web.dll listing47.vb 

Make sure that you enter the entire compile script as one command line. After your assembly is compiled, you must be sure to move the chapter4.dll file to the bin subdirectory of your web application.You’ll see how to reference the Chapter4 namespace in Listings 4.8 and 4.9.

You can now extend the code-behind class shown in Listings 4.2 and 4.3 to include code to reference your StateList user control. This is shown in Listings 4.8 and 4.9.

Listing 4.8. Code-Behind Class Referencing the User Control
using System; 
using System.Web.UI; 
using System.Web.UI.WebControls; 
using Chapter4; 

public class AuthorSearch : Page 
{
      protected Label lbl_author; 
      protected TextBox txt_name; 
      protected StateList uc_statelist; 

      public void btn_name_click(Object sender, EventArgs e) 
      {
           lbl_author. Text = "You are searching for " + txt_name. Text; 
      } 

      public void btn_state_click(Object sender, EventArgs e) 
      {
           lbl_author. Text = "You are searching for authors in " + 
                uc_statelist.SelectedState; 
      } 
} 

Listing 4.9. Code-Behind Class Referencing the User Control (Visual Basic .NET)
Imports System 
Imports System.Web.UI 
Imports System.Web.UI.WebControls 
Imports Chapter4 

Public Class AuthorSearch 
          Inherits Page 

          Protected lbl_author As Label 
          Protected txt_name As TextBox 
          Protected uc_statelist As StateList 

          Public Sub btn_name_click(sender As Object, e As EventArgs) 
                    lbl_author. Text = "You are searching for " & txt_name. Text 
          End Sub 

          Public Sub btn_state_click(sender As Object, e As EventArgs) 
                    lbl_author. Text = "You are searching for authors in " & _ 
                             uc_statelist.SelectedState 
          End Sub 
End Class 

If you apply user controls in your web applications whenever you are going to use the same groups of user interface elements repeatedly, you will drastically reduce the amount of time required to make changes.You will also eliminate the risk of changing one implementation of the user interface and forgetting to change it in the other places where it is used. Less risk and less code equals fewer bugs.You’ll get more thorough coverage of user controls as they pertain to debugging in Chapter 11, “Debugging User Controls .”

Business Objects

Although user controls are great for reuse of user interface elements, they are not really meant for reuse of business logic. That is where traditional business objects come into play. Next you’ll extend your AuthorSearch example to actually retrieve records from the authors table in the pubs database. To do that, you’ll create a business object named AuthorLogic. It will implement a GetAuthorByName method and a GetAuthorByState method to encapsulate the stored procedure calls to retrieve authors from the database.

The GetAuthorByName method will accept an author name and a database connection string. The GetAuthorByState method will accept a state name (its abbreviation, to be exact) and a database connection string. Because you will be connecting to SQL Server, you will use the System.Data.Sql namespace instead of the more generic System.Data .OleDb namespace. Listings 4.10 and 4.11 present the code for your business object.

Listing 4.10. AuthorLogic Object Code
using System; 
using System.Data; 
using System.Data.SQL; 

namespace Chapter4 
{
public class AuthorLogic 
{
      public DataTable GetAuthorByName(string name, string connectString) 
      {
           //construct stored procedure call 
           string sql = "sp_get_author_by_name ' " + name + " ' "; 

           //create a dataset to hold results 
           DataSet ds = new DataSet(); 

           //create connection to database, open it, and retrieve results 
           SQLDataSetCommand cmd = new SQLDataSetCommand(sql,connectString); 
           cmd.FillDataSet(ds, "DataTable"); 

           //return only the DataTable that contains your results 
           return ds. Tables["DataTable"]; 
      } 

      public DataTable GetAuthorByState(string state, string connectString) 
      {
           //construct stored procedure call 
           string sql = "sp_get_author_by_state ' " + state + " ' " ; 

           //create a dataset to hold results 
           DataSet ds = new DataSet(); 

           //create connection to database, open it, and retrieve results 
           SQLDataSetCommand cmd = new SQLDataSetCommand(sql,connectString); 
           cmd.FillDataSet(ds, "DataTable"); 
           //return only the DataTable that contains your results 
           return ds. Tables["DataTable"]; 
      } 
} 
} 

Listing 4.11. AuthorLogic Object Code (Visual Basic .NET)
Imports System 
Imports System.Data 
Imports System.Data.SQL 

Namespace Chapter4 
Public Class AuthorLogic 
      Public Function GetAuthorByName(name As String, _ 
           connectString As String) As DataTable 

           ' construct stored procedure call 
           Dim sql AS String = "sp_get_author_by_name ' " & name & " ' " 

           ' create a dataset to hold results 
           Dim ds As DataSet = New DataSet() 

           ' create connection to database, open it, and retrieve results 
           Dim cmd As SQLDataSetCommand = _ 
                 New SQLDataSetCommand(sql,connectString) 
           cmd.FillDataSet(ds, "DataTable") 

           ' return only the DataTable that contains your results 
           return ds. Tables("DataTable") 
      End Function 

      Public Function GetAuthorByState(state As String, _ 
           connectString As String) As DataTable 

           ' construct stored procedure call 
           Dim sql As String = "sp_get_author_by_state ' " & state & " ' " 

           ' create a dataset to hold results 
           Dim ds As DataSet = New DataSet() 

           ' create connection to database, open it, and retrieve results 
           Dim cmd As SQLDataSetCommand = _ 
                 New SQLDataSetCommand(sql,connectString) 
           cmd.FillDataSet(ds, "DataTable") 

           ' return only the DataTable that contains your results 
           return ds. Tables("DataTable") 
      End Function 
End Class 
End Namespace 

You added your AuthorLogic class definition to your existing Chapter4 namespace (which already contains the code-behind class for your StateList user control). It doesn’t matter that the code resides in separate files.You’ll see when you recompile your assembly that you can specify multiple source code files.

Both methods in the AuthorLogic class are pretty straightforward. They accept arguments, construct stored procedure calls, make the calls, and return the results to the client.

Now that you have added to your Chapter4 namespace, you need to recompile your assembly. For the C# version, use this compile script:

csc /t:library /out:chapter4.dll /r:System.dll;System.Web.dll;System.Data.dll listing46.cs
 listing410.cs 

For the Visual Basic.net version, use this compile script:

vbc /t:library /out:chapter4.dll /r:System.dll,System.Web.dll,System.Data.dll listing47.vb
 listing411.vb 

Make sure that you enter the entire compile script on the same line. The lines are wrapped here because of space constraints.

Next, take a look at Listings 4.12 and 4.13 to see how we have modified the ASP.NET page and its code-behind class to use the AuthorLogic object.

Listing 4.12. Author Search Page Using AuthorLogic Object
<%@ Language="C#" Inherits="AuthorSearch" Src="listing414.cs" %> 
<%@ Register TagPrefix="ch04" TagName="StateList" src="listing45.ascx" %> 
<%@ Import Namespace="System.Data" %> 

<form id="frm_search" method="post" 
     runat="server"> 
Search for an author:<br> 
Name: 
<asp:TextBox id="txt_name" runat="server" /> 
<asp:Button id="btn_name" text="Search" onClick="btn_name_click" 
     runat="server" /> 
<br>OR<br> 
<ch04:StateList id="uc_statelist" runat="server" /> 
<asp:Button id="btn_state" text="Search" onClick="btn_state_click" 
     runat="server" /> 
<br> 
<asp:Label id="lbl_author" name="lbl_author" runat="server" /> 
<br /><br > 
<asp:repeater id="authorList" runat="server"> 
     <template name="ItemTemplate"> 
          <%# ((DataRow)Container.DataItem)["au_fname"]. ToString() %> 
          &nbsp; 
          <%# ((DataRow)Container.DataItem)["au_lname"]. ToString() %> 
          <br /> 
          <%# ((DataRow)Container.DataItem)["address"]. ToString() %> 
          <br /> 
          <%# ((DataRow)Container.DataItem)["city"]. ToString() %>, 
          <%# ((DataRow)Container.DataItem)["state"]. ToString() %> 
          &nbsp; 
          <%# ((DataRow)Container.DataItem)["zip"]. ToString() %> 
          <hr /> 
     </template> 
</asp:repeater> 
</form> 

Listing 4.13. Author Search Page Using AuthorLogic Object (Visual Basic .NET)
<%@ Language="VB" Inherits="AuthorSearch" Src="listing415.vb" %> 
<%@ Register TagPrefix="ch04" TagName="StateList" src="listing45.ascx" %> 
<%@ Import Namespace="System.Data" %> 

<form id="frm_search" method="post" 
     runat="server"> 
Search for an author:<br> 
Name: 
<asp:TextBox id="txt_name" runat="server" /> 
<asp:Button id="btn_name" text="Search" onClick="btn_name_click" 
     runat="server" /> 
<br>OR<br> 
<ch04:StateList id="uc_statelist" runat="server" /> 
<asp:Button id="btn_state" text="Search" onClick="btn_state_click" 
     runat="server" /> 
<br> 
<asp:Label id="lbl_author" name="lbl_author" runat="server" /> 
<br /><br > 
<asp:repeater id="authorList" runat="server"> 
     <template name="ItemTemplate"> 
          <%# CType(Container.DataItem,System.Data.DataRow)("au_fname") %> 
          &nbsp; 
          <%# CType(Container.DataItem,System.Data.DataRow)("au_lname") %> 
          <br /> 
          <%# CType(Container.DataItem,System.Data.DataRow)("address") %> 
          <br /> 
          <%# CType(Container.DataItem,System.Data.DataRow)("city") %>, 
          <%# CType(Container.DataItem,System.Data.DataRow)("state") %> 
          &nbsp; 
          <%# CType(Container.DataItem,System.Data.DataRow)("zip") %> 
          <hr /> 
     </template> 
</asp:repeater> 
</form> 

These two listings look similar to Listing 4.4, with a few additions. For example, there is the addition of an @Import directive for the System.Data namespace. This enables you to reference the objects contained in the System.Data namespace without explicitly typing System.Data as a prefix to its members each time you want to use one. The code-behind class for your author search ASP.NET page binds the Repeater server control to the Rows collection of a DataTable, so you need to be able to cast its individual data items to DataRow objects.

Finally, take a look at how the code-behind class for your author search ASP.NET page uses the AuthorLogic object to get lists of authors and binds it to the Repeater server control. This can be seen in the Listings 4.14 and 4.15.

Listing 4.14. Code-Behind Class for Author Search Page That Uses AuthorLogic Object
using System; 
using System.Web.UI; 
using System.Web.UI.WebControls; 
using Chapter4; 

public class AuthorSearch : Page 
{
     protected Label lbl_author; 
     protected TextBox txt_name; 
     protected StateList uc_statelist; 
     protected Repeater authorList; 

     //normally, you would keep database connection info 
     //in the global.asax file or in an XML configuration file 
     private string connectString = "Password=;User ID=sa;" + 
          "Initial Catalog=pubs;Data Source=localhost;"; 

     //declare your AuthorLogic business object 
     private AuthorLogic al; 

     public void btn_name_click(Object sender, EventArgs e) 
     {
          lbl_author. Text = "You are searching for " + txt_name. Text; 

          //create a new instance of your AuthorLogic business object 
          al = new AuthorLogic(); 

          //assign the results of your search to the repeater control 
          authorList.DataSource = 
                al.GetAuthorByName(txt_name. Text,connectString).Rows; 
          authorList.DataBind(); 
     } 

     public void btn_state_click(Object sender, EventArgs e) 
     {
           lbl_author. Text = "You are searching for authors in " + 
                 uc_statelist.SelectedState; 

          //create a new instance of your AuthorLogic business object 
          al = new AuthorLogic(); 

          //assign the results of your search to the repeater control 
          authorList.DataSource = 
                al.GetAuthorByState(uc_statelist.SelectedState, 
                connectString).Rows; 
          authorList.DataBind(); 
     } 
} 

Listing 4.15. Code-Behind Class for Author Search Page That Uses AuthorLogic Object (Visual Basic .NET)
Imports System 
Imports System.Web.UI 
Imports System.Web.UI.WebControls 
Imports Chapter4 

Public Class AuthorSearch 
      Inherits Page 

      Protected lbl_author As Label 
      Protected txt_name As TextBox 
      Protected uc_statelist As StateList 
      Protected authorList As Repeater 

      ' normally, you would keep database connection info 
      ' in the global.asax file or in an XML configuration file 
      Private connectString As String = "Password=;User ID=sa;" & _ 
           "Initial Catalog=pubs;Data Source=localhost;" 

      'declare your AuthorLogic business object 
      Private al As AuthorLogic 

      Public Sub btn_name_click(sender As Object, e As EventArgs) 
            lbl_author. Text = "You are searching for " & txt_name. Text 

            ' create a new instance of your AuthorLogic business object 
            al = new AuthorLogic() 

            ' assign the results of your search to the repeater control 
            authorList.DataSource = _ 
                 al.GetAuthorByName(txt_name. Text,connectString).Rows 
            authorList.DataBind() 
      End Sub 

      Public Sub btn_state_click(sender As Object, e As EventArgs) 
            lbl_author. Text = "You are searching for authors in " & _ 
                 uc_statelist.SelectedState 

            ' create a new instance of your AuthorLogic business object 
            al = new AuthorLogic() 
             ' assign the results of your search to the repeater control 
             authorList.DataSource = _ 
                   al.GetAuthorByState(uc_statelist.SelectedState, _ 
                   connectString).Rows 
             authorList.DataBind() 
      End Sub 
End Class 

Although setting up all of this might seem like a bit of a hassle at first, the effort pales in comparison to the amount of work that you will do in the long run if you handle every page á la carte. With the introduction of the .NET framework, many sources will tell you that because ASP.NET pages are now compiled, it isn’t necessary to use components. Don’t listen to them. Compiled ASP.NET pages might buy you performance, but good design is what yields maintainability and, most importantly, robust, bug-free code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset