IN THIS CHAPTER
In this chapter, you’ll learn how to create custom components with C# and Visual Basic .NET. Components enable you to reuse the same application logic—the same code—across multiple pages. You can place any type of application logic in a component that you want. For example, you can place all of your database access logic in a component, you can expose an XML Web service from a component, or you can implement custom business logic in a component.
In this chapter, you will learn
The advantages of using components in your Web applications
How to create simple components
How to retrieve database data in a component
How to package components in reusable class libraries
How to access intrinsic ASP.NET objects in components such as the Cache, Trace
, and Session
objects
Before we get started building components, we should pause for a moment and consider whether building components is really necessary. Why bother? What advantages do we get by separating our application into components?
One important advantage of using components is that they enable you to reuse the same application logic across multiple pages. For example, if you need to execute a complicated function to perform a tax calculation on multiple pages, you can place the function in a single component and call the very same function from multiple pages.
Even if you just think that you might want to use the same logic elsewhere, it’s a good idea to package the logic into a separate component. You don’t want to waste time extracting code from existing applications when, all of a sudden, you discover that you need to reuse the code.
Another important advantage of using components is that they enable you to isolate changes that might occur in one part of an application from other parts of an application. In other words, components can make your applications less brittle.
For example, suppose that you build an application that displays sales data. Furthermore, suppose that when you created the application, you stored all the data in an Oracle database. One fine day, a decision is made to transfer all the data from the Oracle database to an SQL Server database.
If you coded all of your database access functions directly into your Web Form Pages, you would need to go back and alter all of the pages. If, on the other hand, you separated the data access functions into a set of data access components, you could modify the components without touching a single Web Form Page or any other part of your application’s business logic.
Finally, placing your application logic into separate components makes it easier for multiple developers to work on a project at the same time. If you divide an application into multiple components, each developer can focus on implementing a particular part of an application without worrying about stumbling over the work of other developers.
For simple applications, you might never need to use components. However, as your applications grow more complicated, dividing your applications into components can make your life significantly easier.
Visual Studio .NET enables you to add two types of entities related to classes to your projects. You can add a C# or Visual Basic .NET component to your project by selecting Add Component from the Project menu, or you can add a C# or Visual Basic .NET class to your project by selecting Add Class from the Project menu.
The only difference between components and classes is that components support the Visual Studio .NET designer interface better than classes. Because components provide you with more flexibility, we’ll build our custom components in this chapter by using components.
Adding a class to your project adds a file with the skeleton for a class declaration. The file automatically contains the following code:
C#.
using System; namespace myApp { /// <summary> /// Summary description for Class1. /// </summary> public class Class1 { public Class1() { // // TODO: Add constructor logic here // } } }
VB.NET.
Public Class Class1 End Class
A component is similar to a class. When you add a new component to a project, the file for the component automatically contains the following code:
C#.
using System; using System.ComponentModel; using System.Collections; using System.Diagnostics; namespace myApp { /// <summary> /// Summary description for Component. /// </summary> public class Component : System.ComponentModel.Component { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public Component(System.ComponentModel.IContainer container) { /// <summary> /// Required for Windows.Forms Class Composition Designer support /// </summary> container.Add(this); InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // } public Component() { /// <summary> /// Required for Windows.Forms Class Composition Designer support /// </summary> InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // } } }
VB.NET.
Public Class Component1 Inherits System.ComponentModel.Component End Class
You’ll notice that the declaration for a component includes some extra code. A component, unlike a class, derives from the System.ComponentModel.Component
class. It also contains a Component Designer generated code region.
The extra code contained in a component declaration enables you to build components by using the Visual Studio .NET Designer interface. You can drag and drop other components from the Toolbox or Server Explorer window onto your component. For example, if you want to add a database connection to your component, you can drag and drop the connection from the Toolbox (see Figure 20.1).
You can also add a component to the Toolbox, but you can’t add a class to the Toolbox. If you want to create a component that you want to easily use in multiple pages, you’ll need to create a component rather than a class.
Let’s start by
creating a simple component that converts inches to millimeters and millimeters to inches. This component will have two methods, ConvertToMillimeters()
and ConvertToInches()
, respectively.
When you create a component in VB.NET, any public functions or subroutines that you declare in the component are exposed as methods. Consequently, to create your component to convert inches and millimeters, you need to create a component that contains two public functions. In C#, you can just create the methods directly.
To do so, perform the following steps:
Procedure 20.1. C# Steps
Select
Add Component from the Project menu. Name the component Converter.cs
and click Open.
Select Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
public decimal ConvertToMillimeters(Decimal Inches) { return Inches * 25.4m; } public decimal ConvertToInches(Decimal Millimeters) { return Millimeters * 0.03937m; }
Procedure 20.2. VB.NET Steps
Select Add Component from the Project menu. Name the component Converter.vb
and click Open.
Select Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
Function ConvertToMillimeters(ByVal Inches As Decimal) As Decimal Return Inches * 25.4 End Function Function ConvertToInches(ByVal Millimeters As Decimal) As Decimal Return Millimeters * 0.03937 End Function
Next, we want to test our component in a Web Form Page. We’ll create a page that contains a form for converting millimeters to inches.
Add a new Web Form Page to your project named TestConverter.aspx
.
Add Label, TextBox
, and Button
controls to the TestConverter.aspx page.
Double-click the Button
control (this will switch you to the Code Editor).
Enter the following code for the Button1_Click
handler:
C#.
private void Button1_Click(object sender, System.EventArgs e) { Converter objConverter = new Converter(); Label1.Text = objConverter.ConvertToInches( Decimal.Parse(TextBox1.Text)).ToString(); }
VB.NET.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim objConverter As Converter objConverter = New Converter() Label1.Text = objConverter.ConvertToInches(TextBox1.Text) End Sub
Right-click the TestConverter.aspx page in the Solution Explorer window and select Build and Browse.
After you create the Web Form Page to test
the component, you should be able to enter a value in millimeters into the text box, click the Button
control, and the value converted to inches should be displayed in the Label
control (see Figure 20.2).
Notice that we need to explicitly declare and create an instance of the component in the Button1_Click
subroutine. After we create an instance of the component, we can simply call the component’s ConvertToInches()
method to convert the contents of the TextBox
control to inches.
You should also notice that IntelliSense works with our new component. When you type the name of an instance of a component followed by a period (for example, objConverter
.), a list of all the
available methods of the component is displayed in a list box.
In the previous section, we created a component for converting inches and millimeters. Before we could use the component in the Web Form Page, however, we were required to explicitly declare and instantiate an instance of the component.
There’s an easier way to use a component in your applications. Instead of declaring instance methods, you can declare static methods (called shared methods in VB.NET). You don’t have to instantiate a component before you call its static methods.
Let’s create a simple component with static methods for converting ounces to grams and grams to ounces.
Procedure 20.3. C# Steps:
Create a new component for
your project named StaticConverter.cs
.
Switch to the Code Editor by selecting Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
public static decimal ConvertToOunces(decimal grams) { return grams * 0.0353m; } public static decimal ConvertToGrams(decimal ounces) { return ounces * 28.35m; }
Procedure 20.4. VB.NET Steps
Create a new component for your project named SharedConverter.vb
.
Switch to the Code Editor by selecting Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
Shared Function ConvertToOunces(ByVal grams As Decimal) As Decimal Return grams * 0.0353 End Function Shared Function ConvertToGrams(ByVal ounces As Decimal) As Decimal Return ounces * 28.35 End Function
Notice that both the ConvertToOunces()
and ConvertToGrams()
methods are declared as static (shared) methods. The method declarations include the keyword Static
or Shared
. Because of the way these methods are declared, we can access the methods from a Web Form Page without creating an instance of the component.
Next, we need to create a Web Form Page to test our component.
Procedure 20.5. C# Steps
Create a new Web Form Page named TestStaticConverter.aspx
.
Add Label, TextBox
, and Button
controls to the page.
Double-click the Button
control (this will switch you to the Code Editor).
Enter the following code for the Button1_Click
subroutine:
private void Button1_Click(object sender, System.EventArgs e) { Label1.Text = StaticConverter.ConvertToGrams(Decimal.Parse(TextBox1.Text)).ToString(); }
Right-click the TestStaticConverter.aspx page in the Solution Explorer window and select Build and Browse.
Procedure 20.6. VB.NET Steps
Create a new Web Form Page named TestSharedConverter.aspx
.
Add Label, TextBox
, and Button
controls to the page.
Double-click the Button
control (this will switch you to the Code Editor)
Enter the following code for the Button1_Click
subroutine:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Label1.Text = SharedConverter.ConvertToGrams(TextBox1.Text) End Sub
Right-click the TestSharedConverter.aspx page in the Solution Explorer window and select Build and Browse.
You’ll notice that we did not explicitly declare the StaticConverter
or SharedConverter
component in the Web Form Page. Because the ConvertToGrams()
method is a static method, we can call the method directly.
After reading this section, you might conclude that it makes sense to create every method as a static (shared) method. However, this type of method has an important limitation—you cannot refer to an instance method or property from a static (shared) method. Consequently, if your component needs to maintain state by using an instance property, you cannot take advantage of static (shared) methods.
As you’ve seen in previous sections, any public function or public subroutine declared in a component is exposed as a method of the component. So far, we’ve discussed how you can add methods to a component, but we have not discussed properties.
You can add properties to a component in two ways—by declaring a public variable or by using property accessor functions. Because using property accessor functions is the preferred method, we’ll use that method here.
We’ll create a component with a single property and a single method. The property, named Language
, will accept a string that represents a language such as French or Indonesian. The SayHello()
method will return the string Hello
in the language specified.
Procedure 20.7. C# Steps
Create a new component for your project named Hello.cs
.
Switch to the Code Editor by selecting Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
Procedure 20.8. VB.NET Steps
Create a new component for your project named Hello.vb
.
Switch to the Code Editor by selecting Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
Dim _language As String Public Property Language() As String Get Return _language End Get Set(ByVal Value As String) __language = Value End Set End Property Function SayHello() As String Select Case _language.ToLower Case "english" Return "Hello" Case "spanish" Return "Hola" Case "french" Return "Bonjour" Case "indonesian" Return "Selamat" End Select End Function
The property is declared in this component by using property accessor functions. The Language
property contains a Get()
method and a Set()
method. When you assign a value to the Language
property, the Set()
method is called. When you read a value from the Language
property, the Get()
method is called.
Notice that we use a private variable, named _language
, with the Language
property. Internal to our component, we use the _language
property instead of the Language
property.
To test our component, we need to create a new Web Form Page:
Create a new Web Form Page named TestHello.aspx
.
Add Label, TextBox
, and Button
controls to the TestHello.aspx page.
Double-click the Button
control (this will switch you to the Code Editor).
Enter the following code for the Button1_Click
handler:
C#.
private void Button1_Click(object sender, System.EventArgs e) { Hello objHello = new Hello(); objHello.Language = TextBox1.Text; Label1.Text = objHello.SayHello(); }
VB.NET.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim objHello As Hello objHello = New Hello() objHello.Language = TextBox1.Text Label1.Text = objHello.SayHello() End Sub
Right-click the TestHello.aspx page in the Solution Explorer window and select Build and Browse.
If you enter the text French
into the text box and click the button, the text Bonjour
should appear in the Label
control
(see Figure 20.3).
Because you can take full advantage of the Visual Designer when building a component, you easily add all of your database logic to a component. In this section, we’ll create a simple database component that retrieves all the records from the Products database table.
Procedure 20.9. C# Steps
Create a component named Products.cs
.
Drag
the Products database table from under the Northwind data connection onto the Designer surface. Doing this will add both an SqlConnection
and SqlDataAdapter
to the component.
Add an Untyped DataSet to the component.
Switch to the Code Editor by selecting Code from the View menu.
Enter the following method in the body of the component:
public DataSet GetProducts() { sqlDataAdapter1.Fill(dataSet1); return dataSet1; }
Add the following two statements to the top of the component:
using System.Data; using System.Data.SqlClient;
Procedure 20.10. VB.NET Steps
Create a component named Products.vb
.
Drag the Products database table from under the Northwind data connection onto the Designer surface. Doing this will add both an SqlConnection
and SqlDataAdapter
to the component.
Add an Untyped DataSet to the component.
Switch to the Code Editor by selecting Code from the View menu.
Enter the following function in the body of the component:
Function GetProducts() As DataSet SqlDataAdapter1.Fill(DataSet1) Return DataSet1 End Function
That was easy, wasn’t it? We only need two statements to return a DataSet.
Next, we’ll create a test page that grabs the data from the Products component and displays the data in a DataGrid.
Create a new page named TestProducts.aspx
.
Add a DataGrid to the page.
Switch to the Code Editor by selecting Code from the View menu.
Enter the following code for the Page_Load
handler:
C#.
private void Page_Load(object sender, System.EventArgs e) { Products objProducts = new Products(); DataGrid1.DataSource = objProducts.GetProducts(); DataGrid1.DataBind(); }
VB.NET.
Right-click the TestProducts.aspx page in the Solution Explorer window and select Build and Browse.
Notice that the TestProducts.aspx page does not include any database components such as a DataSet or SqlConnection. All the database components are hidden away in the Products component.
In the previous section, we created a database component that returned database records with a DataSet. However, you should be aware that retrieving records with a DataAdapter and DataSet is significantly slower, in almost all situations, than retrieving the same records with a DataReader. In this section, we’ll take a look at how we can modify our database component to use a DataReader instead of a DataSet.
Using a DataReader can be two to three times faster than using a DataSet (depending on the number of rows being requested from the database). To learn more about the speed differences between a DataReader and a DataSet, see the “Performance Comparison: Data Access Techniques” article posted at the msdn.microsoft.com Web site at http://msdn.microsoft.com/library/en-us/dnbda/html/bdadotnetarch031.asp.
Procedure 20.11. C# Steps
Create a new component named ProductsDR.cs
.
Drag the Data Connection to the Northwind database from the Server Explorer window onto the Designer surface.
Add an SqlCommand
object to the page.
In the Properties window, double-click next to the Connection
property to assign the Northwind database connection to the SqlCommand
object.
Assign
the following SQL SELECT
command to
the SqlCommand
object’s CommandText
property:
SELECT * FROM Products
Switch to the Code Editor by selecting Code from the View menu.
Enter the following method in the body of the component:
public SqlDataReader GetProducts() { sqlConnection1.Open(); return sqlCommand1.ExecuteReader(CommandBehavior.CloseConnection); }
Add the following two statements to the top of the component:
using System.Data; using System.Data.SqlClient;
Procedure 20.12. VB.NET Steps
Create a new component named ProductsDR.vb
.
Drag the Data Connection to the Northwind database from the Server Explorer window onto the Designer surface.
Add an SqlCommand
object to the page.
In the Properties window, double-click next to the Connection
property to assign the Northwind database connection to the SqlCommand
object.
Assign the following SQL SELECT
command to the SqlCommand
object’s CommandText
property:
SELECT * FROM Products
Switch to the Code Editor by selecting Code from the View menu.
Enter the following method in the body of the component:
Public Function GetProducts() As SqlDataReader SqlConnection1.Open() Return SqlCommand1.ExecuteReader(CommandBehavior.CloseConnection) End Function
Add the following statement to the top of the component:
Imports System.Data.SqlClient
We can bind the data returned from the ProductsDR component to a DataGrid in exactly the same way as we bound the data returned by the Products component in the previous section. However, the data is retrieved much faster with a DataReader than a DataSet.
Notice that when we call the ExecuteReader()
method, we pass the special parameter CommandBehavior.CloseConnection
. This parameter causes the database connection being used by the DataReader to close automatically after all the records are retrieved by the DataReader. If we did not include this parameter, the database connection would never be explicitly
closed.
As an application grows more complex, you’ll need to divide it into multiple tiers. The traditional method of doing this is to use the three-tiered application model. According to this model, an application should be divided into the following tiers:
Presentation Tier—. Contains the user interface for the application
Business Tier—. Contains the business rules for the application
Data Tier—. Contains the data access components for the application
In an ASP.NET application, you use Web Form Pages for the presentation tier. The business and data tiers are typically implemented with business components.
In this section, we’ll create a simple three-tiered application by creating separate components for the business and data tiers. We’ll create a simple order entry application.
We’ll work our way up the tiers starting from the data tier. For the data tier, we’ll create a component that writes orders to the file system. To create the data component, do the following:
Procedure 20.13. C# Steps
Create a new component named DataCom.cs
.
Switch to the Code Editor by selecting Code from the View menu.
Add
the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
public static void SaveOrder(string OrderID, decimal OrderAmount) { System.IO.TextWriter objWriter; objWriter = System.IO.File.AppendText("c:\Orders.txt"); objWriter.WriteLine("OrderID: " + OrderID); objWriter.WriteLine("OrderAmount:" + OrderAmount.ToString()); objWriter.Close(); }
Procedure 20.14. VB.NET Steps
Create a new component named DataCom.vb
.
Switch to the Code Editor by selecting Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
Public Shared Sub SaveOrder(ByVal OrderID As String, ByVal OrderAmount As Decimal) Dim objWriter As System.IO.TextWriter objWriter = System.IO.File.AppendText("c:Orders.txt") With objWriter .WriteLine("OrderID: " & OrderID) .WriteLine("OrderAmount:" & OrderAmount) End With objWriter.Close() End Sub
The DataCom component has a single method, named SaveOrder
, that saves an order ID and order amount to a file named Orders.txt located at the root of the C:
drive.
Next, we’ll create the business tier by creating a single business component. The business component will implement the business rule that you cannot enter an order for an amount less than $1.00 or an order greater than $500.00.
Procedure 20.15. C# Steps
Create a new component for your project named BizCom.cs
.
Switch to the Code Editor by selecting Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
public static bool AddOrder(string OrderID,decimal OrderAmount) { if (OrderAmount > 1 && OrderAmount < 500) { DataCom.SaveOrder(OrderID, OrderAmount); return true; } else return false; }
Procedure 20.16. VB.NET Steps
Create a new component for your project named BizCom.vb
.
Switch to the Code Editor by selecting Code from the View menu.
Add the following code to the body of the component (do not remove the section labeled Component Designer generated code
):
Public Shared Function AddOrder(ByVal OrderID As String, ByVal OrderAmount As Decimal) As Boolean If OrderAmount > 1 And OrderAmount < 500 Then DataCom.SaveOrder(OrderID, OrderAmount) Return True Else Return False End If End Function
The BizCom
class checks whether the OrderAmount
is greater than 1 and less than 500. If the order passes the check,
the BizCom component calls the DataCom component to save the order.
Finally, we need to create the presentation tier. We’ll create the presentation tier by creating a Web Form Page named Presentation.aspx:
Add a new Web Form Page to your project named Presentation.aspx
.
Add an HTML Label with the text Order ID
.
Add a TextBox
control.
Add a second HTML Label with the text Order Amount
.
Add a second TextBox
control.
Add a Web Forms Label
control.
Add a Web Forms Button
control.
Add a Button1_Click
handler by double-clicking the Button
control (this will switch you to the Code Editor).
Enter the following code for the Button1_Click
handler:
C#.
private void Button1_Click(object sender, System.EventArgs e) { if (BizCom.AddOrder(TextBox1.Text, Decimal.Parse(TextBox2.Text)) ) { Label1.Text = "Order Added!"; TextBox1.Text = ""; TextBox2.Text = ""; } else Label1.Text = "Could Not Add Order!"; }
VB.NET.
Right-click the Presentation.aspx page in the Solution Explorer window and select Build and Browse.
If you enter a value between 1 and 500 into the Order Amount text box, and submit the form, a new order is added to the Order.txt file. If you do not enter a valid order amount, the error message “Could Not Add Order!” is displayed (see Figure 20.4).
To get the most out of using components, you’ll want to reuse the same components in multiple projects. In other words, you’ll want to develop a standard library of components that you can use in your projects.
When you add a component (or a class) to your project, the component is compiled into the same assembly as the rest of your project. For example, if you are working on a project named myWebApp, the component is compiled into the myWebApp.dll assembly when you compile the project.
An assembly is the actual file (or files) on your hard drive that contains the application logic for your project. When you compile a project by selecting Build and Browse, the project is compiled into an assembly.
If you want to use the same component in multiple projects, you’ll need to compile the component into an independent assembly. You can create a component that is independent from any particular Web Application project by creating a Class Library project. A single class library can contain multiple components.
To do so, perform the following steps:
Procedure 20.17. C# Steps
Open the File menu, and select New, Project.
Select the Visual C# Projects folder and select the Class Library template. Name the project MyLibrary
and click OK.
Delete the Class1.cs file from the MyLibrary project by right-clicking the Class1.cs file in the Solution Explorer window and selecting Delete.
Add a new component to the MyLibrary project named GeneralHello.cs
.
Enter the following code for the GeneralHello component (do not modify the section
labeled Component Designer generated code
):
public string SayHello() { return "Hello!"; }
Compile the component by selecting Build MyLibrary from the Build menu.
Procedure 20.18. VB.NET Steps
Open the File menu, and select New, Project.
Select the Visual Basic Projects folder and select the Class Library template (see Figure 20.5). Name the project MyLibrary
and click OK.
Delete the Class1.vb file from the MyLibrary project by right-clicking the Class1.vb file in the Solution Explorer window and selecting Delete.
Add a new component to the MyLibrary project named GeneralHello.vb
.
Enter the following code for the GeneralHello component (do not modify the section labeled Component Designer generated code
):
Public Function SayHello() As String Return "Hello!" End Function
Compile the component by selecting Build MyLibrary from the Build menu.
After completing these steps, you’ll have
a new component named GeneralHello. The component has one method—SayHello()
—that simply returns the string Hello!
.
The component is stored in an assembly on your hard drive named MyLibrary.dll. If you want to use the GeneralHello component in another project, you need to add a reference to this assembly to the project:
Create a new ASP.NET Web Application project named MyProject
by selecting New Project from the File menu.
Add a reference to the GeneralHello assembly by selecting Add Reference from the Project menu. Select the Projects tab and click the Browse button. Browse to the MyLibrary.dll assembly and click Open (see Figure 20.6). The MyLibrary.dll assembly will be located at the following path:
My DocumentsVisual Studio ProjectsMyLibraryinDebug
Add a new Web Form Page to the project named TestGeneralHello.aspx.
Add a Label
control to the TestGeneralHello.aspx page.
Switch to the Code Editor by selecting Code from the View menu.
Enter the following code for the Page_Load
handler:
C#.
private void Page_Load(object sender, System.EventArgs e) { Label1.Text = MyLibrary.GeneralHello.SayHello(); }
VB.NET.
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Label1.Text = myLibrary.GeneralHello.SayHello End Sub
Right-click the TestGeneralHello.aspx page and select Build and Browse.
The TestGeneralHello.aspx page uses the GeneralHello component to display the text Hello!
in a Label
control. You can use the GeneralHello component in the TestGeneralHello.aspx page even though the component is not defined in the same project. Because you added a reference to the GeneralHello.dll assembly, the component is available to the project.
This means that if you want to use a class library developed by a friend, your friend would simply need to email you the assembly. All the information about the class library is contained in the assembly.
Any references that you add to a project appear under the References folder in the Solution Explorer window.
If you create a genuinely useful set of components that you plan to use with multiple projects, you’ll want to add the components to the Toolbox. Do the following to add the components in the MyLibrary project to the Toolbox:
Open the TestGeneralHello.aspx page.
Right-click the Toolbox and select Add Tab. Enter the name MyLibrary
for the new tab.
Right-click beneath your new tab on the Toolbox and select Customize Toolbox.
In the Customize Toolbox dialog box, select the .NET Framework Components tab.
Click Browse and navigate to the MyLibrary.dll file on your hard drive. It should be in the following directory:
My DocumentsVisual Studio ProjectsMyLibraryinDebug
Click OK.
After you complete these steps, the GeneralHello component will appear in the Toolbox beneath the MyLibrary tab. If you want to use the GeneralHello component in a page, you can simply drag and drop the component from the Toolbox onto the page. After the component has been added to the page, it will appear in the component bar at the bottom of the Designer window (see Figure 20.7).
When you drag a component onto a page, code for declaring and initializing the component is automatically added to the code-behind class for the page. For example, if you add the GeneralHello component to a page, the following two statements are added:
C#.
protected MyLibrary.GeneralHello generalHello1; this.generalHello1 = new MyLibrary.GeneralHello(this.components);
VB.NET.
Protected WithEvents GeneralHello1 As MyLibrary.GeneralHello Me.GeneralHello1 = New MyLibrary.GeneralHello(Me.components)
The declaration statement is added to the top of the code-behind file for the page and the initialization statement is added to the Web Form Designer generated code region.
Within
a Web Form Page, you have access to all the information about the current request. You have direct access to objects such as the Request, Response, Session, User, Trace
, and Cache
objects. Within a component, on the other hand, you must do a little work before you access these objects.
Before you can access the intrinsic ASP.NET objects, you must first retrieve a reference to the current HttpContext. The HttpContext
object has properties that expose all the standard ASP.NET intrinsic objects.
For example, suppose that you want to create a database component that caches database data in the server’s memory. Instead of accessing the database with each database query, the component will check the cache first. To create this component, you’ll need some way of referring to the Cache
object. You can get to the Cache
object by getting a reference to the current HttpContext.
Perform the following steps to create the CachedData component:
Procedure 20.19. C# Steps
Create a new component named CachedData.cs
.
Drag the Products table from under the Northwind Data Connection onto the Designer surface. This will add both an SqlConnection
and SqlDataAdapter
to the component.
Add an Untyped DataSet to the component.
Switch to the Code Editor by selecting Code from the View menu.
Enter the following method:
public DataSet GetResults(string CommandText) { // Get reference to current HttpContext HttpContext Context = HttpContext.Current; // Try to retrieve data from Cache dataSet1 = (DataSet)Context.Cache[CommandText.ToLower()]; // If can't, get from database if (dataSet1 == null) { Context.Trace.Warn("Retrieving data from DB"); dataSet1 = new DataSet(); sqlDataAdapter1.SelectCommand.CommandText = CommandText; sqlDataAdapter1.Fill(dataSet1); Context.Cache[CommandText.ToLower()] = dataSet1; } else Context.Trace.Warn("Retrieving data from memory"); // In either case, return the DataSet return dataSet1; }
Add the following statements to the top of the component:
using System.Web; using System.Data; using System.Data.SqlClient;
Procedure 20.20. VB.NET Steps
Create a new component named Cacheddata.vb
.
Drag the Products table from under the Northwind Data Connection onto the Designer surface. This will add both an SqlConnection
and SqlDataAdapter
to the component.
Add an Untyped DataSet to the component.
Switch to the Code Editor by selecting Code from the View menu.
Enter the following method:
Function GetResults(ByVal CommandText As String) As DataSet ' Get reference to current HttpContext Dim Context As HttpContext = HttpContext.Current ' Try to retrieve data from Cache DataSet1 = Context.Cache(CommandText.ToLower()) ' If can't, get from database If (DataSet1 Is Nothing) Then Context.Trace.Warn("Retrieving data from DB") DataSet1 = New DataSet() SqlDataAdapter1.SelectCommand.CommandText = CommandText SqlDataAdapter1.Fill(DataSet1) Context.Cache(CommandText.ToLower()) = DataSet1 Else Context.Trace.Warn("Retrieving data from memory") End If ' In either case, return the DataSet Return DataSet1 End Function
The first line in the GetResults()
method returns a reference to the current HttpContext. Notice that after the reference is retrieved, both the Cache
and Trace
objects can be used.
The CachedData component first attempts to retrieve a DataSet representing the results of an SQL query from the cache. If the results are not present in the cache, the component retrieves the results from the database and adds the results to the cache. In either case, the results are returned.
You can test the component by building the following page:
Add a page to your project named TestCachedData.aspx
.
Select Document in the Properties window and assign the value FlowLayout
to the pageLayout
property and the value True
to the Trace
property.
Add a TextBox
, Web Forms Button
, Web Forms Label
control, and DataGrid to the page.
Double-click the Button
control to add a Button1_Click
handler and switch to the Code Editor.
Enter the following code for the Button1_Click
handler:
C#.
private void Button1_Click(object sender, System.EventArgs e) { CachedData objCachedData = new CachedData(); DataGrid1.DataSource = objCachedData.GetResults(TextBox1.Text); DataGrid1.DataBind(); }
VB.NET.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim objCachedData As New CachedData() DataGrid1.DataSource = objCachedData.GetResults(TextBox1.Text) DataGrid1.DataBind() End Sub
Right-click TestCachedData.aspx in the Solution Explorer window and select Build and Browse.
When the page opens, you can type any SELECT
statement into the text box and click the button to see the results in the DataGrid (see Figure 20.8). For example
SELECT * FROM Categories
When you execute the SELECT
statement for the first time, the results are retrieved from the database. Subsequently, the results are retrieved from the cache. You can verify this by looking at the Trace Information
section.
In this chapter, you learned how to create both C# and VB.NET business components. First, you learned how to create simple business components by adding methods and properties to a component. You also learned how to create a simple business component that retrieves database data.
Next, we looked at how you can use components to divide an application into multiple tiers. We created a simple multi-tier application by creating components for the business and data tiers.
We then examined how you can package a library of components in a separate assembly. You learned how to create a separate Class Library project and refer to the class library in an ASP.NET Web Application project. You also learned how you can add components to the Toolbox.
Finally, we discussed the importance of the HttpContext
object. You learned how to use the HttpContext
object to refer to ASP.NET intrinsics, such as the Cache
and Trace
objects within a component.