Categorization by Functionality

Snoops

   

 
Migrating to .NET: A Pragmatic Path to Visual Basic .NET, Visual C++ .NET, and ASP.NET
By Dhananjay Katre, Prashant Halari, Narayana Rao Surapaneni, Manu Gupta, Meghana Deshpande

Table of Contents
Chapter 6.  Migrating to ASP.NET I


To carry out an actual migration, we must break up the application into some logical parts . This helps in modularizing the approach to migration and can also help the developer to decide how much of the existing application should be migrated . We have divided functionality in the following categories:

  • GUI- related changes

  • Database-related changes

  • Error handling

  • Handling COM components

  • Session state management

  • Cache control

Following categorization by functionality, we will discuss the changes in ASP.NET related to intrinsic objects and structural and language changes in Visual Basic .NET.

Web Form Server Controls

The .NET Framework class library provides an excellent support for GUI through the System.Web.UI namespace. This consists of all the controls including Web server and user controls. The Web server controls can be divided into the following categories:

  • Intrinsic controls . Those that map to the HTML controls

  • List controls . Those that provide data access throughout the page

  • Rich controls . Those that provide rich UI and functionality

  • Validation controls . Those that provide a variety of data validation techniques

Out of these, intrinsic controls map directly onto the HTML controls, whereas list and validation controls have no equivalent in ASP. Rich controls in ASP.NET are an improved version over the ASP equivalents.

Intrinsic controls, also known as HTML server controls, are especially useful for migrating ASP applications to ASP.NET. In the following subsections we will take a look at HTML server controls as a replacement for HTML controls.

INTRINSIC CONTROLS

An HTML element can be easily converted to a server control by adding the runat =server attribute. As a general rule, Web server controls are more capable and have a richer object model than HTML server controls. These controls can detect the browser and device capabilities and render themselves accordingly . This is important when we are not sure of the requesting browser type or even the device type. If an existing Web-based application being accessed from a PC is migrated with the intention of making it available to mobile devices or PDAs, changing HTML controls to Web server controls will be a required part of the migration. But extensive and indiscriminate use of Web server controls can lead to performance issues.

HTML server controls are useful for situations in which you will be programming a control both on the server and on the client because the control will be identical in both runtime environments. This makes it easier to write client scripts for the control and also makes it available for use by validation controls. HTML server controls are best suited in situations where a lot of client-side validations are being made, and at the same time the control is required to raise server-side events. It may be best to leave HTML controls as they are in cases where such a requirement is not mandatory.

In Table 6-1, the "HTML Control in ASP" column gives the HTML syntax for controls; the equivalent HTML server controls and Web server controls are also given.

Table 6-1. Server Controls

Control Name

HTML Control in ASP

HTML Server Control in ASP.NET

Web Server Control in ASP.NET

Textbox

<Input type="text" name="text1" id="text1">

<Input type="text" name="text1" id="text1" runat="server">

<Asp: Textbox ID="text1" runat="server"/>

Password

<Input type="password" name="txtpass" id="tpass" >

<Input type="password" name="txtpass" id="tpass" runat="server">

<Asp: textbox textmode="password" id="tpass" runat="server"/>

Hidden

<Input type="hidden" text="hidden" id="hdn1" name="texthdn">

<Input type="hidden" text="hidden" id="hdn1" name="texthdn" runat="server">

No equivalent

File

<Input type="file" name="file1" id="file1" >

<Input type="file" name="file1" id="file1" runat="server">

No equivalent

TextArea

<Text Area id="textarea1" name="textarea1" ></ textarea>

<TextArea id="textarea1" name="textarea1" runat = "server"></textarea>

<asp: Textbox Height="50" Width="105" Wrap="True" MaxLength="3" TextMode="MultiLine" Columns="20" Rows="5" Runat="server" ID="textarea1"> </asp: Textbox>

Checkbox

<INPUT id="checkbox1" type="checkbox" name="checkbox1" checked="true">

<INPUT id="checkbox1" type="checkbox" name="checkbox1" runat="server" checked="true">

<Asp: CheckBox ID="checkbox1" Runat="server" Checked="True"></asp: CheckBox>

Radio button

<INPUT id="radio1" type="radio" name="radio1" checked="true">

<INPUT id="radio1" type="radio" name="radio1" runat="server" checked="true">

<Asp: RadioButton ID="radio1" runat="server" Checked="True"> </asp: RadioButton>

Dropdown box

<SELECT id="select1" name="select1"> <OPTION value="1" selected ID="Option1" NAME="Option1">PCS </OPTION><OPTION value="2" ID="Option2" NAME="Option2">Ebiz </OPTION> </select>

<SELECT id="select1" name="select1" runat="server"> <OPTION value="1" selected runat="server" ID="Option1" NAME="Option1">PCS </OPTION> <OPTION value="2" runat="server" ID="Option2" NAME="Option2">Ebiz </OPTION> </SELECT>

<Asp: DropDownList ID="cmb1" Runat="server"> <Asp: ListItem Value="1" Text="PCS" Selected> </asp: ListItem> <Asp: ListItem Value="2" Text="ebiz"> </asp: ListItem> </asp: DropDownList>

List box control

<SELECT id="select2" size ="2" name="select2" > <OPTION value="1" selected >PCS</OPTION> <OPTION value="2" >PCS </OPTION> </SELECT>

<SELECT id="select2" size="2" name="select2" runat="server"> <OPTION value="1" selected runat="server">PCS</OPTION> <OPTION value="2" runat="server">PCS</OPTION> </SELECT>

<Asp: ListBox ID="list1" DataTextField="ketal" Rows="5" SelectionMode="Single" Runat="server"> <asp: ListItem Selected Text="PCS" Value="1"> </asp: ListItem> <asp: ListItem Selected Text="CS" Value="2"> </asp: ListItem> </asp: ListBox>

Submit button

<INPUT id="submit1" type="submit" value="Submit" name="submit1" >

<INPUT id="submit1" type="submit" value="Submit" name="submit1" runat="server">

<Asp: Button ID="submit1" Runat="server" Text="Submit"> </asp: Button>

Reset button

<INPUT id="reset1" type="reset" value="Reset" name="reset1" > reset

<INPUT id="reset1" type="reset" value="Reset" name="reset1" runat="server"> reset

No equivalent

Button

<INPUT id="button1" type="button" value="Button" name="button1">

<INPUT id="button1" type="button" value="Button" name="button1" runat="server">

<Asp: Button ID=" button1" Runat="server" Text="Click Here"></asp: Button>

Label

<LABEL> <STRONG>Label in ASP</STRONG> </ LABEL>

No equivalent

<asp:Label ID="name" Runat="server" AccessKey></asp:Label>

Table

<table cellpadding ="1" cellspacing="1" border="1"> <tr> <td>ABC</td> <td>DEF</td> </tr> </table>

<table cellpadding="1" cellspacing="1" border="1" runat="server"> <tr> <td>ABC</td> <td>DEF</td> </tr> </table>

<asp:Table CellPadding="1" CellSpacing="1" Runat="server" BorderStyle="Solid" BorderWidth="1" GridLines="Vertical"> <asp:TableRow Runat="server" HorizontalAlign="Center"> <asp:TableCell Runat="server" Text="ABC" HorizontalAlign="Center"> </asp:TableCell> <asp:TableCell Runat="server" Text="DEF" HorizontalAlign="Center" ></asp:TableCell> </asp:TableRow> </asp:Table>

Href

<A href="login.asp" >Hello</a>

 

<A href="login.aspx" runat="server">Hello</a>

Image

<Img src="/ControlsNet/ HELP.BMP" /> IN ASP

<Img src="/ControlsNet/ HELP.BMP" runat="server" /> IN ASP

<Asp: Image ImageUrl= "/ControlsNet/HELP.BMP" Runat="server"> </asp: Image>

Image button

No equivalent

No equivalent

<Asp:ImageButton Runat="server" ImageUrl="http://local host/ ControlsNet/HELP.BMP" ID="Imagebutton1" NAME="Imagebutton1"> </asp:ImageButton>

Form

<Form action="// aspx" method="get" name="form1" > </form>

<Form action="//aspx" method="get" name="form1" runat="server"></form>

<Form action="//aspx" method="get" name="form1" runat="server"></form>

RadioButtonList

No equivalent

No equivalent

<Asp: RadioButtonList Runat="server" Width> <Asp: ListItem Text="Male" Value="m">Male </asp: ListItem> <Asp: ListItem Text="Female" Value="m">Female </asp: ListItem> </asp: RadioButtonList>

Appendix B contains details of all properties for the HTML controls that are and are not supported in the equivalent server controls in ASP.NET.

Data Access

Many ASP applications commonly make use of ADO for handling database activity. Under the .NET Framework, ADO.NET takes care of all the database activities, but we can continue using ADO in ASP.NET by adding a reference to the ActiveX Data Object Type Library in the ASP.NET Web application. This way the developer can retain existing code using ADO.

It is, however, not recommended that you import and use ADO in .NET applications; if you must, you should have good reasons for doing so. In situations where a lot of working ADO code already exists in an application and a lot has been invested in business objects that use ADO to read and write, ADO should not be replaced with ADO.NET.

If you have a lot of ADO code that you want to preserve, you must first determine where that code is located and whether it is in ASP pages buried in plain-code blocks or in COM business objects that return recordsets to the ASP presentation builder layer. For performance and maintenance reasons, placing plain ADO code in ASP pages is a bad programming practice. In such cases, the ADO code is nearly legacy code. Such code can be adapted to work in .NET, but some performance and coding issues can crop up.

The code changes needed to use ADO with ASP.NET will be mainly due to the difference in the way objects are created in ASP and ASP.NET. In ASP, we use late binding to create objects, whereas ASP.NET supports early binding. The following example shows creation of a connection object using late binding in traditional ASP:

 graphics/icon01.gif <%     Dim con     Set con = server.CreateObject("ADODB.Connection")  %> 

In ASP.NET this line can be written using early binding as shown:

 graphics/icon01.gif <%     Dim con as ADODB.Connection     con = new ADODB.Connection()  %> 

If a number of applications are sharing the same database, performance is a major issue. Also, transmitting data across a number of applications is a problem caused by a lack of standards in the manner in which data can be sent and received. This is a perfect scenario for migrating ADO that is being used in the existing application accessing the shared database to ADO.NET. ADO.NET uses XML for transmitting data and hence any system that is able to parse XML can send and receive data using ADO.NET. Firewalls pose a problem when transmitting data using recordsets. This is resolved in ADO.NET because XML can easily pass through firewalls, and hence data transmission in ADO.NET is a lot easier than in ADO.

Consider an existing Web application for attendance recording and monitoring that is available through an intranet within an organization. The application has a centralized database and the number of users accessing it is limited to employees of that organization. The application makes use of ADO for all the data handling and needs to be migrated for a richer appearance and increased concurrent user access. Even with an increased number of users, performance may not pose a major issue if we continue using ADO in the migrated application.

To make use of the features provided by .NET, the developer should rewrite the parts of the code related to data access. The first step in that direction would be to import the namespace System.Data into the page. The System.Data namespace consists mostly of the classes that constitute ADO.NET architecture. ADO.NET architecture enables you to build components that efficiently manage data from multiple data sources. The namespaces used with ADO.NET are listed in Table 6-2.

A .NET data provider describes a collection of classes used to access a data source, such as a database, in the managed space.

Table 6-2. Namespaces used with ADO.NET

Namespace

Contains

System.Data

Base objects and types for ADO.NET

System.Data.OleDb

Managed OLE DB DataStore objects

System.Data.SqlClient

SQL Server-specific implementations of ADO.NET objects

System.Data.SQLTypes

Data types specific to SQL Server

The System.Data.OleDb namespace is the OLE DB .NET data provider. It provides classes that make up the OLE DB data provider. The OLE DB data provider allows you to connect to an OLE DB data source, execute commands against the source, and read the results.

The System.Data.SqlClient namespace is the SQL Server .NET data provider. It is similar to the System.Data.OleDb namespace, but is optimized for access to SQL Server 7 and later.

The following examples will clarify the use of .NET data access components in your code. The following code example demonstrates the use of the OLE DB data provider by executing a select statement to retrieve data. In the ASP sample shown, data is retrieved into a recordset. The recordset is then iterated through and its contents displayed in an HTML table.

The Web Application CodeSamples contains an ASP file named DataAccess.asp.

 graphics/icon01.gif <%     Dim con     Dim strSQL     Dim rsLogin     Set con = server.CreateObject("ADODB.Connection")     con.CursorLocation = 3     con.Open "SampleDSN","sa",""     strSQL = "Select * from Login"     Set rsLogin = server.CreateObject("ADODB.recordset")     rsLogin.Open strSQL,con     rsLogin.MoveFirst  %>     <html>        <head>           <title>DataAccess</title>       </head>       <body>       <form id="Form1">       <table border=1>  <%           Response.Write "<TR>"           Response.Write "<TD>"           Response.Write rsLogin.Fields(0).Name           Response.Write "</TD>"           Response.Write "<TD>"           Response.Write rsLogin.Fields(1).Name           Response.Write "</TD>"           Response.Write "</TR>"           While not rsLogin.EOF              Response.Write "<TR>"              Response.Write "<TD>"              Response.Write rsLogin("login_name")              Response.Write "</TD>"              Response.Write "<TD>"              Response.Write rsLogin("pwd")              Response.Write "</TD>"              Response.Write "</TR>"             rsLogin.MoveNext           Wend  %>       </table>       </form>       </body>     </html> 

This code can also be used in ASP.NET by adding ADO Type Library in the reference as mentioned. But this way we cannot make use of the features provided by .NET.

In ASP.NET we have used the dataset to store the retrieved data. The dataset is the core component of the disconnected architecture of ADO.NET. It is explicitly designed for data access independent of any data source. A dataset is made up of a collection of data table objects, each data table object representing a database table. Data is stored in the data tables in XML format. A dataset allows for nonsequential access to data by indexing it, which is not possible in a sequentially accessed recordset. It also allows for data manipulation, including updates, that can be later reconciled with the database.

In the following ASP.NET example, data is retrieved into a dataset. A datagrid control is used to display data from the dataset. The datagrid control is designed for use with data in the form of dataview, dataset, or datareader objects. It generates a visible interface in the form of HTML table, automatically adding column or item names as the header row. Data binding is used to display data in the datagrid.

The Web application CodeSamples contains an ASP.NET file named DataAccess.aspx, which demonstrates the following example:

 graphics/icon01.gif <%@ Page Language="vb" %>  <%@ Import Namespace="System.Data"%>  <%@ Import Namespace="System.Data.OleDb"%>     <HTML>        <HEAD>        <title>Data Access</title>  <%     Dim strConnect as String     Dim strSQL as String     Dim OLEds as new DataSet()     Dim objOLEConnection as OLEDBConnection     Dim objOLEda as OLEDBDataAdapter        strConnect = _        "server=localhost;uid=sa;pwd=;database=Samples;        Provider=SQLOLEDB"        strSQL = "select * from Login"        objOLEConnection = New OLEDBConnection (strConnect)        objOLEda = new OLEDBDataAdapter (strSQL, objOLECon _        nection)        objOLEda.Fill(OLEds)        DataGrid1.DataSource = OLEds        DataGrid1.DataBind ()  %>        </HEAD>        <body>        <form id="Form1" method="post" runat="server">           <asp:DataGrid ID="DataGrid1" Runat="server"           style="LEFT: 10px; POSITION: absolute; TOP:           15px"></asp:DataGrid>        </form>        </body>     </HTML> 

As seen in the example, the whole exercise of writing an unformatted table in HTML for displaying data can be replaced by two lines of code using a datagrid. The example demonstrates the use of a Web server control datagrid in conjunction with ADO.NET by making use of its databind property. A datagrid is commonly used with ADO.NET when data is to be displayed in tabular format. Other server controls used in conjunction with ADO.NET are datalist and repeater controls.

The preceding code can be written using a datareader object. A datareader provides a read-only, forward-only cursor over data. This is particularly useful in applications that process a series of results, often requiring no user interaction or no updating or scrolling back through the result set as it is being read. The datareader object is designed just for such a scenario where it is not necessary to update the data source or cache data locally as with the ADO recordset.

To use the datareader object, we declare an OLEDBCommand object instead of the OLEDBDataAdapter , as seen in the previous example. We again use the concept of data binding to display data from the datareader. The Web application CodeSamples contains an ASP.NET file named DataAccess.aspx , which demonstrates the following example:

 graphics/icon01.gif <%@ Page Language="vb" %>  <%@ Import Namespace="System.Data"%>  <%@ Import Namespace="System.Data.OleDb"%>     <HTML>        <HEAD>        <title>Data Access</title>  <%     Dim strConnect as String     Dim strSQL as String     Dim OLEds as new DataSet()     Dim objOLEConnection as OLEDBConnection     Dim objOLEda as OLEDBDataAdapter        strConnect = _        "server=localhost;uid=sa;pwd=;database=Samples;        Provider=SQLOLEDB"        strSQL = "select * from Login"        objOLEConnection = New OLEDBConnection (strConnect)        objOLEda = new OLEDBDataAdapter (strSQL, objOLECon _        nection)        objOLEda.Fill(OLEds)        DataGrid1.DataSource = OLEds        DataGrid1.DataBind ()  %>        </HEAD>        <body>        <form id="Form1" method="post" runat="server">           <asp:DataGrid ID="DataGrid1" Runat="server"           style="LEFT: 10px; POSITION: absolute; TOP:           15px"></asp:DataGrid>        </form>        </body>     </HTML> 

The example in ASP, which demonstrates the use of dataset and datareader, is not actually a perfect candidate for migration. It merely showcases the use of ADO.NET together with the list control datagrid.

The preceding code is shown as written in .aspx pages. This can also be written in the code-behind for the page (i.e., .aspx.vb page). The use of the code-behind technique is demonstrated in the case study where we call a certain COM method in the Page_Load event of the page. Although the code-behind makes programming easy by separating out HTML from the logic, this might prove to be time consuming for developers who have worked in the ASP environment.

Both of the preceding ASP.NET examples can be written for a SQL database using the SqlConnection and SqlCommand objects instead of OLEDBConnection and OLEDBCommand . Also, SqlDataReader can be used instead of OLEDBDataReader . SqlDataReader provides a forward-only, read-only pointer over data retrieved from a SQL database as against DataSet , which allows operating on the data it holds and then reconciles it with the database. To use a SqlDataReader , declare a SqlCommand object instead of a SQLDataAdapter .

The preceding example demonstrates the use of OLEDB data provider. The following examples demonstrate the use of the SQLClient data provider. These examples can also be written using OLEDB . We have just seen some typical examples of retrieving data and displaying it. We will now take a look at an insert/update/delete statement being executed using ADO.NET. The example demonstrates a typical update command being executed using ADO in ASP. The Web application CodeSamples contains an ASP file named DataAccessUpdate.asp , which demonstrates the following example :

 graphics/icon01.gif Dim con  Dim strSQL  Dim objCommand     Set con = _     server.CreateObject("ADODB.Connection")     con.CursorLocation = 3     con.Open "SampleDSN","sa",""     strSQL="Update Login set pwd='789'"     Set objCom _     mand=Server.CreateObject("ADODB.Command")     objcommand.ActiveConnection = con     objCommand.CommandText = strSQL     Call objCommand.Execute(intRecords) 

This can be written using ADO.NET as follows . The Web application CodeSamples contains an ASP.NET file named DataAccessUpdate.aspx, which demonstrates the following example:

 graphics/icon01.gif Dim objsqlConnection as SQLConnection  Dim objsqlCommand as SQLCommand  Dim strConnect as String  Dim strCommand as String     strConnect = _     "server=localhost;uid=sa;pwd=;database=Samples"     strCommand = "UPDATE Login set pwd ='1234'"     objsqlConnection = New SQLConnection (strConnect)     objsqlCommand = new SQLCommand (strCommand, _     objsqlConnection)     objsqlCommand.Connection.Open()     objsqlCommand.ExecuteNonQuery()     objsqlCommand.Connection.Close() 

When performing commands that do not require data to be returned, such as inserts , updates, and deletes, you can use the SqlCommand object as demonstrated in this example. The command is issued by calling an ExecuteNon-Query method, which returns the number of rows affected.

We will now look at migrating a sample code in ASP using ADO. The example tries to retrieve data from a data source into a recordset. If no records are returned, an error message is displayed. The Web application CodeSamples contains an ASP file named DataAccessError.asp , as demonstrated in the following example:

 graphics/icon01.gif <%     Dim objConn     Dim objRs     Dim strQry        Set objConn = server.CreateObject("ADODB.Connection")        objConn.CursorLocation = 3        objConn.open "SampleDSN","sa",""        strQry = "Select * from Login"        Set objRs = server.CreateObject("ADODB.recordset")        objRs.open strQry, objConn        If objRs.EOF then           Response.Write "No record exists"        Else           Response.Write "Records exist"        End If  %> 

To migrate, we first rename the file to DataAccessError.aspx . If we visit this page in a browser, we will get the error message shown in Figure 6-6.

Figure 6-6. Error : Let and Set are not supported.

graphics/06fig06.gif

The .NET Framework does not support keywords Let and Set ; these are used to create the connection and command objects in the ASP code. After removing these keywords, we visit the page and get the error message shown in Figure 6-7.

Figure 6-7. Error: Method calls should be enclosed in parentheses.

graphics/06fig07.gif

Visual Basic .NET requires that all method calls, including Response. Write , must be enclosed in parentheses. After putting parentheses at all the required places, we visit the page again. This time we get an error message as shown in Figure 6-8. This error occurs because the ADO objects are marked in the registry as apartment threaded. When trying to use such an apartment-threaded component through an ASP.NET page, the apartment-threaded component cannot be created.

Figure 6-8. Error: Apartment threaded components only with attribute aspcompat=true.

graphics/06fig08.gif

ASP.NET offers an ASP compatibility mode, as the error message says. To turn on the ASP compatibility mode, add the aspcompat=true attribute to the Page directive; however, this can affect the performance of the ASP.NET page adversely.

 <%@ Page language="vb" aspcompat="true"%> 

By using this attribute, the ASP.NET page executes in a single-threaded apartment (STA) mode. This shows that ASP.NET provides backward compatibility with ASP intrinsic objects.

After making this change, the ASP.NET page runs successfully. The final code in the ASP.NET page after migration follows. The Web application CodeSamples contains an ASP.NET file named DataAccessError.aspx , which demonstrates the following example:

 graphics/icon01.gif <%@ Page Language="vb" aspcompat="true"%>  <%     Dim objConn     Dim objRs     Dim strQry        objConn = server.CreateObject("ADODB.Connection")        objConn.CursorLocation = 3        objConn.open ("SampleDSN","sa","")        strQry = "Select * from Login"        objRs = server.CreateObject("ADODB.recordset")        objRs.open (strQry, objConn)        If objRs.EOF then           Response.Write ("No record exists")        Else           Response.Write ("Records exist")        End If  %> 

By using the attribute aspcompat=true , we have seen that the changes required in the ASP.NET code are minimal. But using this attribute will degrade the performance of the ASP.NET to some extent. Hence, it should be used only where absolutely necessary. If this attribute is not used, the code for the ASP.NET page for the same example will look as follows. The Web application CodeSamples contains an ASP.NET file named DataAccessError1.aspx , which demonstrates the following example:

 graphics/icon01.gif <%@ Page language ="vb"%>  <%@ Import Namespace="System.Data"%>  <%@ Import Namespace="System.Data.SqlClient"%>  <%     DIM strQry as String     DIM strConnect as String     DIM objConn as SQLConnection     DIM objCommand as SQLDataAdapter     DIM objRs as new Dataset()     DIM objTable = new DataTable()        strConnect = _        "server=localhost;uid=sa;pwd=;database=Samples"        objConn = new SQLConnection(StrConnect)        strQry = "Select * from Login"        objCommand = new SQLDataAdapter (strQry, objConn)        objCommand.Fill (objRs)        objTable = objRs.Tables(0)        If objTable.Rows.Count > 0 then           Response.Write ("Records exist")        Else           Response.Write ("No record exists")        End If  %> 

Thus, it can be seen that without using the attribute aspcompat=true in the Page directive, migration of an ASP page using ADO amounts to rewriting it using ADO.NET. It is a good idea to use the attribute initially during migration; later on when the schedule permits , you may decide to rewrite that part of the application to make use of the features provided by the .NET Framework.

In traditional Web applications, we can use ADO to make calls to a stored procedure. This is also possible with ADO.NET; it is described in the case study where we use stored procedures written for a SQL Server database.

Error Handling and Debugging Techniques

One of the new features of ASP.NET is its rich support for handling and tracking runtime errors. Specifically , it provides an easy way for administrators to ensure that the ASP 43433ax hex-style errors never get displayed to customers, instead a customizable error message can be displayed.

In ASP, developers using VBScript rely heavily upon On Error Resume Next or On Error goto 0 statements. Although this works very well, it is not a very neat way of handling exceptions, and writing centralized routines for error handling becomes difficult. The CLR has solved this difficulty by providing support for structured error handling in the form of a try...catch...finally block. Although On Error statements are still supported, this provides a far better way of handling exceptions.

USING ON ERROR STATEMENTS

The On Error statements are still supported for backward compatibility. The example shown is a simple ASP page explicitly creating an error condition and handled using On Error Resume Next . The Web application CodeSamples contains the ASP file ErrorHandler.asp , which contains the following code:

 graphics/icon01.gif <html>  <head>  <title>ErrorHandler</title>  <script language=vbscript runat=server>     Sub checkError()        on error resume next        Dim obj        obj.movefirst()        If (Err.number <> 0) then           Response.Write "Error occurred"        End If     End Sub  </script>  </head>  <body>     <% checkError%>  </body>  </html> 

The same code can run under ASP.NET with almost no changes. The Web application CodeSamples contains the ASP.NET file ErrorHandler.aspx , which contains the following code:

 graphics/icon01.gif <%@ Page Language="vb" %>  <html>      <head>            <title>ErrorHandler</title>            <script runat="server">              Sub checkError()                 on error resume next                 Dim obj                 obj.movefirst()                 If (Err.number <> 0) then                    Response.Write ("Error occurred")                 End If              End Sub            </script>      </head>      <body>        <%Call checkError()%>      </body>  </html> 

This example can be written using On Error goto statement in ASP.NET as shown. The following code appears in the ASP.NET page ErrorHandler.aspx in the Web application CodeSamples .

 graphics/icon01.gif <%@ Page Language="vb" %>  <html>      <head>            <title>ErrorHandler</title>            <script runat="server">              Sub chkError()                 on error goto errHandler                 Dim obj                 obj.movefirst()                 errHandler:                  Response.Write("Error occurred")              End Sub            </script>      </head>      <body>            <%Call chkError()%>     </body>  </html> 
USING CUSTOM ERROR PAGES

In ASP, errors are handled by trapping the HTTP error encountered and redirecting the user to a specific error page. In ASP.NET, the configuration file web.config can be configured to redirect to a specific page whenever the error is encountered.

The configuration setting that follows indicates that in the event of any unhandled error, the browser should be redirected to the ErrorPage.aspx page.

 graphics/icon01.gif <configuration>     <customerrors mode="On" defaultRedi_     rect="ErrorPage.aspx" />  </configuration> 

The defaultredirect attribute on the <customerrors> tag defines the "default" page that a user will be redirected to in the event of an error.

The Web.config file settings apply to its directory and any child directories. These settings can be overridden for specific pages using the ErrorPage attribute of the Page directive. An example follows:

 <%@ Page ErrorPage="customerror.aspx" %> 

Optionally, as shown in the following, you can choose to override the configuration settings with alternative pages to redirect to depending upon the HTTP status code of the response:

 graphics/icon01.gif <configuration>     <customerrors defaultredi _   rect="http://anotherhost/error.aspx" mode="remoteonly">        <error statuscode="500" _        redirect="http:/anotherhost/pages/support.html" _        />        <error statuscode="404" redi _        rect="http:/anotherhost/pages/adminmessage.html" _        />     </customerrors>  </configuration> 

The <customerrors> tag supports a mode attribute with three values:

  • on. Means a custom error page is always sent out.

  • off. Means a custom error page is never sent out (you always see the original error message).

  • remoteonly. Means a custom error page is only sent out when remote browsers hit the site (developers hitting the site on the actual machine see the detailed error message).

The following examples demonstrate the use of custom error pages in ASP and ASP.NET. In this ASP example, the user is redirected to the custom error page Errorpage.asp after checking for the error number. The Web application CodeSamples contains the ASP page ErrorRedirect.asp that contains the following code:

 graphics/icon01.gif <html>     <head>        <title>ErrorRedirect</title>        <script language=vbscript runat=server>           on error resume next           Dim obj           obj.getmessasge()           If Err.number<> 0 then               Response.Redirect "ErrorPage.asp"           End If        </script>     </head>     </html> 

ErrorPage.asp is as follows. This ASP page is included in the Web application CodeSample :

 graphics/icon01.gif <html>  <head>     <title>ErrorPage</title>  </head>  <body>     <H3>This is the custom error page. </H3>  </body>  </html> 

This can be done in ASP.NET as shown in the example. The Web application CodeSamples contains the ASP.NET file ErrorRedirect.aspx that demonstrates the following example.

 graphics/icon01.gif <%@ Page Language="vb" %>     <html>     <head>        <title>ErrorRedirect</title>        <script runat=server>           Dim obj           obj.getmessage()        </script>     </head>     </html> 

The <customerrors> section in the web.config is as follows:

 graphics/icon01.gif <customErrors mode="On" defaultRedirect="ErrorPage.aspx"/> 

ErrorPage.aspx is as follows. This ASP.NET page is included in the Web application CodeSamples :

 graphics/icon01.gif <%@ Page Language="vb" %>  <html>      <head>            <title>ErrorPage</title>      </head>      <body>            <H3>This is the custom error page. </H3>      </body>  </html> 
STRUCTURED EXCEPTION HANDLING

Error handling in ASP is limited to On Error statements. The .NET Framework provides structured exception handling as a fundamental part of the CLR and equips developers with a much better way of handling exceptions than ASP could provide. An example of structured exception handling using trycatch...finally follows. This example is demonstrated in the ASP.NET page ErrorHandler.aspx in Web application CodeSamples :

 graphics/icon01.gif <%@ Page Language="vb" %>  <html>      <head>            <title>ErrorHandler</title>            <script runat="server">              Sub catchError()                 Try                    Dim obj                    obj.getmessage()                 Catch                    Response.Write("Error occurred")                 End Try              End Sub            </script>      </head>      <body>         <%Call catchError()%>      </body>  </html> 

All exception classes are derived from the Exception base class within the System namespace for compliance with the CLS. The .NET Framework class library provides an extensive hierarchy of exception classes to handle various types of common exceptions, all of which ultimately derive from Exception .

The try block contains code that could throw an exception. The catch block can contain a filter on the types of exceptions that can occur within the code block. The filter is specified following the keyword catch . There can be multiple catch blocks and these should be ordered from the most specific type to the most generic. This ensures the most specific type catch block is executed for any given exception. The finally statement executes, allowing clean up and other code to be executed, regardless of whether an exception is thrown.

The following example shows multiple catch blocks with the generic Exception class at the last catch block. This example is demonstrated in ErrorHandler.aspx in Web application CodeSamples :

 graphics/icon01.gif <%@ Page Language="vb" %>  <html>      <head>            <title>ErrorHandler</title>            <script runat="server">              Sub catchExp()                 Dim x as integer = 0                 Dim y as integer                 Try                    y = 25/x                 Catch aex as ArithmeticException                    Response.Write(aex.message)                 Catch ex as Exception                    Response.Write(ex.message)                 End Try              End Sub            </script>      </head>      <body>            <%Call catchExp()%>      </body>  </html> 

In this example, the first catch block filters on type ArithmeticException . The exception thrown is of the same type and hence is handled by the first catch block itself. The generic exception block is never executed in this case.

Tracing and Debugging

Tracing is a feature that did not exist in ASP and is now introduced in ASP.NET. It involves tracing the execution of an application. The following example will clarify this concept. It shows a method written in VBScript for multiplying two numbers. To know the values of the numbers being processed we add Response.Write statements within the method. The Web application CodeSamples contains an ASP file Trace.asp , which demonstrates the following:

 graphics/icon01.gif <html>  <head>  <title>Trace</title>  <script language=vbscript runat=server>  function muliplyNum(a, b)     Response.Write "Inside function a : " & a & "<BR>"     Response.Write "Inside function b : " & b & "<BR>"     muliplyNum = a*b  end function  </script>  </head>  <body>     Result of 4*5 = <%=muliplyNum(4,5)%>  </body>  </html> 

The output of this code as seen in the browser is as follows:

 Result of 4*5 =  Inside function a : 4  Inside function b : 5  20 

This introduces a lot of unwanted code, which requires clean up after the development stage.

ASP.NET introduces the Trace object, which serves a similar purpose without the introduction of unwanted code. The Web application CodeSamples contains an ASP.NET file Trace.aspx , which demonstrates the following:

 graphics/icon01.gif <%@ Page Language="vb" Trace="true"%>  <%@ Import Namespace="System.Diagnostics"%>     <html>     <head>        <title>Trace</title>        <script runat=server>        function muliplyNum(a, b) as integer           Trace.Write("Inside function a:" , a.ToString())           Trace.Write("Inside function b:" , b.ToString())           Return a*b        end function        </script>     </head>     <body >        Result of 4*5 = <%=muliplyNum(4,5)%>     </body>  </html> 

We have enabled page tracing here by adding the attribute Trace to the Page directive. The namespace System.Diagnostics containing the Trace object is included in the page. Notice that the Response.Write statements are replaced by the Write method of the Trace object. The output of the page is as shown in Figure 6-9.

Figure 6-9. Output of page using the Trace object.

graphics/06fig09.gif

Tracing can be configured at the application level in the configuration file web.config . This is useful when the output of several pages needs to be traced. This way we do not have to add the Trace attribute to the Page directive. The <trace> section of the web.config can be configured as shown:

 <trace enabled="true" _  requestLimit="10" _  pageOutput="false" _  traceMode="SortByTime" _  localOnly="true" /> 

We can now use a special tool trace.axd to view the application traces. The application trace is shown in Figure 6-10. On clicking the link View Details the detailed page output, as shown in the figure, can be seen. This can be achieved by setting the attribute pageOutput to True.

Figure 6-10. Application-level traces.

graphics/06fig10.gif

An advantage of using the Trace object is that the trace output appears locally only when using the Trace attribute of the Page directive. This can be controlled in the web.config by changing the value of the localOnly attribute.

Debugging with ASP involves mainly Response.Write statements, as we saw earlier, inserted throughout the page to track execution of the page. The Script Debugger , a part of the development environment, is not as effective as the debugger for Visual Basic or C++. The scenario is changed now with the CLR introducing integrated debugging.

To enable debugging in an ASP.NET page, we add the Debug attribute to the Page directive, just like we added the Trace attribute for tracing:

 <%Page Language="vb" Debug="True"%> 

This tells the compiler to emit debugging symbols in the compiled page, and it allows the debugger to attach to the running program.

In Visual Studio .NET, debugging can be carried out by simply adding breakpoints to certain lines in the code and running the application in Debug mode. This also gives us the facility to edit code while debugging is on.

Remote debugging works in the same way as local debugging with the difference being that you will be debugging a remote process. This is particularly useful when there is a separate Web server for hosting applications.

Interoperability in .NET

The major advantage of ASP.NET applications (and other applications developed in the Microsoft .NET environment) is that they can interoperate very well with existing COM components. This is achieved with the help of the COM interoperability layer provided by the Microsoft .NET Framework. The interoperability mechanism allows application developers to preserve their investments in existing COM components and reuse these components in the new ASP.NET applications.

In this section we take a brief look at the interoperability features provided by .NET, which allow the use of unmanaged code within the .NET application. They also allow the use of managed components within unmanaged COM-based code. We will look at using these features through Visual Studio .NET. There are certain command-line tools used specifically for interoperation between the managed and unmanaged environments.

The .NET CLR enables interoperability by hiding the complexity associated with calls between managed and unmanaged code. The runtime automatically generates code to translate calls between the two environments. Interoperability is covered in detail in Chapter 10.

USING COM IN MANAGED CODE

When a COM object is called from .NET, the runtime generates a Runtime Callable Wrapper ( RCW ). The RCW acts as a proxy for the unmanaged object; it is responsible for handling all interaction between the .NET client code and the COM component. The RCW manages the creation and binding of COM objects, data marshalling between the environments, and consuming COM interfaces.

The RCW is a managed object and is allocated from the heap maintained by the CLR. It is subject to garbage collection just like any other object managed by the CLR.

We will now look at using COM components in an ASP.NET Web application through Visual Studio .NET. The COM DLL used in the following example ( MyComp.dll ) is registered on the Web server.

Create the Interop assembly through Visual Studio .NET as follows:

  1. Open the project in Visual Studio .NET. In the solution explorer, right-click on References and click on Add Reference. See Figure 6-11.

    Figure 6-11. Adding a reference to a Web project.

    graphics/06fig11.gif

  2. Select the COM tab and choose the required DLL, in this case MyComp.dll . If the DLL is not listed browse and select the DLL. See Figure 6-12.

    Figure 6-12. Selecting COM DLL.

    graphics/06fig12.gif

  3. Click OK to add the selected DLL to the project. See Figure 6-13.

    Figure 6-13. Adding selected DLL to the project.

    graphics/06fig13.jpg

  4. Visual Studio .NET generates the assembly for the selected COM DLL and places it in the /bin directory of the Web application. The COM DLL is added as a reference and appears in the solution explorer as shown in Figure 6-14. Also seen is the /bin directory with the Interop assembly.

    Figure 6-14. COM DLL added as reference.

    graphics/06fig14.gif

Instead of letting Visual Studio .NET create the Interop assembly, the user can explicitly create it by using a command-line tool known as the Type Library Importer ( tlbimp.exe ). For this, go to the command prompt and change to C:\Program Files\Microsoft Visual Studio .Net\FrameworkSDK\Bin directory. Enter the following command:

 tlbimp <path1>/mycomp.dll /out:<path2>/mycompnew.dll 

Here, <path1> is the path of the COM DLL and <path2> represents the path where .NET assembly needs to be placed. This is usually the /bin directory of the ASP.NET application in which we want to use the COM component. The assembly now created acts as the proxy between the managed code and the unmanaged COM component.

Consuming the assembly in a .NET Client

The following code example uses the .NET assembly just like any other component. The Web application CodeSamples contains an ASP.NET file TestCOM.aspx , which is demonstrated in the following example:

 graphics/icon01.gif <%@ Page Language="vb" %>     <html>     <script runat=server>        sub createObject           Dim objClass as MyComp.MyClass           objClass = new MyComp.MyClass()           Response.Write (objClass.getmessage())        end sub     </script>     <head>     <title>TestCOM</title>     </head>     <body>        <% Call createObject()%>     </body>     </html> 

This example demonstrates early binding. The same can be written in traditional style using late binding by adding the attribute aspcompat="true" to the Page directive as follows:

 graphics/icon01.gif <%@ Page Language="vb" aspcompat="true"%>     <html>     <script runat=server>        Sub createObj           Dim objClass           objClass=server.createObject("MyComp.MyClass")           Response.Write (objClass.getmessage())        End Sub     </script>     <head>     <title>TestCOM</title>     </head>     <body >        <% Call createObj()%>     </body>     </html> 

ASP.NET offers a compatibility mode by way of the aspcompat attribute. When this attribute is added to the Page directive, the ASP.NET page is allowed to use apartment-threaded COM components as in traditional ASP.

If the assembly is created through the tlbimp.exe and installed in the /bin directory of the Web application, we can refer to it by using the @Import directive as shown in the following.

The Web application CodeSamples contains an ASP.NET file TestCOM.aspx , which demonstrates the following example:

 graphics/icon01.gif <%@ Page Language="vb" aspcompat="true"%>  <%@ Import Namespace="mycompnew"%>  <html>      <script runat="server">        Sub importObject           Dim objClass as mycompnew.MyClass           objClass = new mycompnew.MyClass ()           Response.Write (objClass.getmessage())        End Sub      </script>      <head>            <title>TestCOM</title>      </head>      <body>            <% Call importObject()%>      </body>  </html> 

An alternative to the Import directive is to link the assembly to the project through the configuration file. The automatic linking is enabled by means of the <assemblies> section of the configuration file ( web.config ):

 graphics/icon01.gif <configuration>    <compilation>      <assemblies>        <add assembly="mycompnew"/>      </assemblies>    </compilation>  </configuration> 

The ASP.NET code will then appear as follows:

 graphics/icon01.gif <%@ Page Language="vb" aspcompat="true"%>  <html>      <script runat="server">        Sub importObject           Dim objClass as mycompnew.MyClass           objClass = new mycompnew.MyClass ()           Response.Write (objClass.getmessage())        End Sub      </script>      <head>            <title>TestCOM</title>      </head>      <body>      <%Call importObject()%>      </body>  </html> 

COM components can be used in ASP.NET applications by creating an RCW around them. The RCW is responsible for marshalling data and acts as a proxy between the managed and unmanaged code. The RCW can be created through Visual Studio .NET or by using a command-line tool, the Type Library Importer.

USING .NET COMPONENTS IN UNMANAGED CODE

When calling .NET components in COM, the CLR generates a COM Callable Wrapper ( CCW ), which acts as a proxy between the managed component and the unmanaged code. The CCW is responsible for marshalling data types and providing access to the .NET class in the COM.

To operate with the CCW the .NET component's assembly must be signed with a strong name; otherwise the CLR will not be able to identify it definitively. The strong name provides version identification; this is how the CLR can allow multiple versions of the same component to reside within the process. The assembly must also follow the standard .NET component location convention (i.e., the assembly must reside in the global assembly cache or at least in the client application's directory tree.)

Any .NET class that could be used in COM should provide a default constructor, (i.e., one without any parameters). This is required because COM object creation functions do not know how to pass parameters to the objects that they create.

A .NET developer could reasonably want only some methods , interfaces, or classes to be available to COM clients . Therefore, .NET provides a metadata attribute called System.Runtime.InteropServices.ComVisible . This attribute can be used on an assembly, a class, an interface, or an individual method. The CCW reads this metadata attribute at runtime and will fail any request from COM to access anything marked with this attribute set to False. Settings made lower in the hierarchy override those made higher up.

A .NET component is created as follows:

  1. Create a .NET project of type ClassLibrary .

  2. Define a class name and provide a default constructor within the class.

  3. Define and write the code for methods and properties within the class.

In the following code example, the project file named NETinCOM contains a class Class1 :

 graphics/icon01.gif Public Class Class1      Public Function GetMessage ()          GetMessage = "Hello from .NET COM"      End Function  End Class 

Create a strong name file (.snk) for the preceding class as follows:

  1. On the command prompt, go to directory in which the sn.exe is residing. It is usually in Program Files/Microsoft.NET/FrameworkSDK/ bin directory.

  2. Type the following on the command prompt:

     C:\Program Files\Microsoft.NET\FrameworkSDK\Bin>sn -k _  <path>netkey.snk 

    Here, Sn invokes the sn.exe and -K <outfile> generates a new key pair and writes it into <outfile> specified by <path>.

  3. This strong name key file name needs to be included in the assembly as the assembly key file attribute. This is done by adding the following lines in the class file, before the class declaration:

     Imports System.Reflection<Assembly: AssemblyKeyFileAttribute  ("C:\Migration_Book\Chapters\Chapter11\NETinCOM _ \Bin\net- key.snk")> 

The strong name key file is now generated and referenced in the code. Generate the type library as follows:

  1. Add the metadata attribute System.Runtime.InteropServices. ComVisible as follows:

     <System.Runtime.InteropServices.ComVisible (True)>Public _  Class Class1 
  2. Build the assembly for this class.

  3. To generate the type library and make registry entries of the assembly as required for the COM, the assembly is run through the Assembly Registration Tool ( regasm.exe ). This program reads the metadata in a .NET class and makes registry entries that point the COM client to it. Type the following lines at the command prompt:

     C:\Program Files\Microsoft.NET\FrameworkSDK\Bin>regasm _  <path1>NETinCOM.dll /tlb: <path2>NETinCOM.tlb 

    Here, regasm invokes the regasm.exe , <path1> represents path of the existing COM DLL, and /tlb [: FileName] exports the assembly to the type library specified in <path2> and registers it.

    This can be alternatively done using the Type Library Exporter ( tlbexp.exe ). The tlbexp.exe is a command-line tool that converts the classes and interfaces contained in an assembly to a COM type library.

     C:\Program Files\Microsoft.NET\FrameworkSDK\Bin>tlbexp _  <path1>NETinCOM.dll /out: <path2>NETinCOM.tlb 

    Here, tlbexp invokes the tlbexp.exe and /out: FileName is the file name of type library to be produced at the path specified in <path2>.

  4. The .NET assembly now needs to be moved in the global assembly cache. At the command prompt type the following:

     C:\Program Files\Microsoft.NET\FrameworkSDK\Bin>gacutil _  /i: <path>NETinCOM.dll 

    Here, gacutil invokes the gacutil.exe , /i installs an assembly to the global assembly cache, and requires the name of the file containing the manifest as a parameter.

Visual Studio .NET provides an alternative way of creating the .tlb file. Under Project Properties check the Register for COM Interop checkbox before building the assembly. When the assembly is created, the type library file (.tlb) is generated simultaneously and the registry entries are also done, as shown in Figure 6-15.

Figure 6-15. Checking Register for COM Interop in Visual Studio .NET.

graphics/06fig15.gif

Using the .NET Component

By adding the assembly to the global assembly cache, we have made it available outside the .NET Framework. In an ASP application that wants to use this component, the code will appear as follows:

 Dim obj  Set obj = server. CreateObject ("NETinCOM.Class1")  Response. Write obj.getmessage () 

The .NET component can be used in any traditional COM client such as a Visual Basic application or an ASP Web application by putting a CCW around the .NET component. The CCW can be created through Visual Studio .NET or through the command-line tools tlbexp.exe or regasm.exe .


Snoops

   
Top


Migrating to. NET. A Pragmatic Path to Visual Basic. NET, Visual C++. NET, and ASP. NET
Migrating to. NET. A Pragmatic Path to Visual Basic. NET, Visual C++. NET, and ASP. NET
ISBN: 131009621
EAN: N/A
Year: 2001
Pages: 149

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net