Building Components

Components enable you to reuse application logic across multiple pages or even across multiple applications. For example, you can write a method named GetProducts() once and use the method in all the pages in your website. By taking advantage of components, you can make your applications easier to maintain and extend. For simple applications, there is no reason to take advantage of components. However, as soon as your application contains more than a few pages, you’ll discover that you are repeating the same work over and over again. Whenever you discover that you need to write the same method more than once, you should immediately rip the method out of your page and add the method to a component.

In classic ASP, programmers often used massive and difficult to maintain #INCLUDE files to create libraries of reusable subroutines and functions. In ASP.NET, you use components to build these libraries.


In this chapter, you learn how to build components in the .NET Framework. First, you are provided with an overview of writing components: You learn how to create simple components and use them in the pages in your application. In particular, you learn how to define component methods, properties, and constructors. You also learn how to take advantage of overloading, inheritance, and interfaces.

Next, you learn how to build component libraries that can be shared across multiple applications. Different methods of compiling a set of components into assemblies are examined. You also learn how you can add a component library to the Global Assembly Cache.

Finally, architectural issues involved in using components are discussed. The final section of this chapter shows you how to build a simple three-tiered application that is divided into distinct User Interface, Business Logic, and Data Access layers.

Let’s clarify the terminology. In this book, I use the word component as a synonym for the word class. Furthermore, by the word object, I mean an instance of a class. I am ignoring a special meaning for the word component in the .NET Framework. Technically, a component is a class that implements the System.ComponentModel.IComponent interface. I am ignoring this special meaning of the word component in favor of the common language use of the word.

Building Basic Components
Let’s start by building a super simple component. The HelloWorld component is contained in Listing

public class HelloWorld
{
public string SayMessage()
{
return “Hello World!”;
}
}


The HelloWorld component consists of a single method named SayMessage() which returns the string Hello World!.

When using Visual Web Developer, you create a component by selecting the menu option Website, Add New Item, and then selecting the Class item. The first time you add a component to a project, Visual Web Developer prompts you to create a new folder named App_Code. You want your new component to be added to this folder.

Components and Dynamic Compilation
You are not required to explicitly compile (build) the component because the ASP.NET Framework automatically compiles the component for you. Any component that you add to the App_Code folder is compiled dynamically in the same way as an ASP.NET page. If you add a new component to the App_Code folder and request any page from your website, the contents of the App_Code folder are compiled into a new assembly and saved to the Temporary ASP.NET Files folder, located at the following path:

“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\[application name]”

Whenever you modify the component, the existing assembly in the Temporary ASP.NET Files folder is deleted. The App_Code folder is compiled again when you make a new page request.

An assembly is the dll file (or dll files) in which components are stored. You can add as many subfolders to the App_Code folder as you need to organize your components. The ASP.NET Framework finds your component no matter how deeply you nest the component in a subfolder.

One significant drawback of this process of dynamic compilation is that any errors in any component contained in the App_Code folder prevent any pages from executing. Even if a page does not use a particular component, any syntax errors in the component raise an exception when you request the page.

If a component contains an error, and you want to temporarily hide the component from the ASP.NET Framework, change the file extension to an extension that the ASP.NET Framework does not recognize, such as HelloWorld.cs.exclude. Visual Web Developer uses this method to hide a component when you right-click a component and select the menu option Exclude From Project.

Mixing Different Language Components in the App_Code Folder
You don’t have to do anything special, just as long as all the components in the App_Code folder are written in the same language. For example, if you use Visual Basic .NET to create all your components, then the ASP.NET Framework automatically infers the language of your components and everything just works.

However, if you mix components written in more than one language in the App_Code folder—for example, Visual Basic .NET, and C#—then you must perform some extra steps. First, you need to place components written in different languages in different subfolders. You can name the subfolders anything you want. The point is to not mix different language components in the same folder. Furthermore, you need to modify your web configuration file to recognize the different subfolders. For example, if you create two subfolders in the App_Code folder named VBCode and CSCode, then you can use the web configuration file to use components written in both VB.NET and C#.

When the contents of the App_Code folder are compiled, two assemblies are created: one that corresponds to the VBCode folder and one that corresponds to the CSCode folder. Notice that you don’t need to indicate the language used for each folder—the ASP.NET Framework infers the language for you. There is nothing wrong with mixing components written in different languages in the same ASP.NET page. After a component is compiled, the .NET Framework treats VB.NET and C# components in the same way.

Declaring Methods
The simple HelloWorld component in Listing contains a single method named SayMessage(), which returns a string value. When writing components with Visual Basic .NET, you create methods by creating either a subroutine or a function. Use a subroutine when a method does not return a value, and use a function when a method does return a value.

The SayMessage() method is an instance method. In other words, you must create a new instance of the HelloWorld class before you can call the SayMessage(), method like this:

HelloWorld objHelloWorld = new HelloWorld();
lblMessage.Text = objHelloWorld.SayMessage();

In the first line, a new instance of the HelloWorld component is created. The SayMessage() method is called from this instance. For this reason, the SayMessage() method is an instance method. As an alternative to creating an instance method, you can create a static method. The advantage of a static method is that you do not need to create an instance of a component before calling it. For example, the SayMessage() method in the modified HelloWorld component is a static method. Static methods are called shared methods in Visual Basic .NET.

Declaring Fields and Properties
You can define a property for a component in two ways: the lazy way and the virtuous way. The lazy way to create a property is to create a public field. If you declare any field with the Public access modifier, then the field can be accessed from outside the component.

There are a couple of serious disadvantages to creating properties by creating public fields. First, the .NET Framework recognizes properties as separate entities. Several methods in the .NET Framework recognize properties but not fields. For example, you can refer to component properties and not fields when using the Eval() method in a databinding expression. If you want to bind a collection of Product objects to a GridView control, then you should expose the properties of the Product component as true properties and not as fields.

The other disadvantage of fields is that they do not provide you with a chance to validate the value being assigned to the field. For example, imagine that a property represents a database column and the column accepts no more than five characters. In that case, you should check whether the value being assigned to the property is less than five characters. The component in Listing 15.8 uses a property instead of a field. (It does things the virtuous way.)

The version of C# included with the .NET Framework 3.5 supports a new feature named automatic properties. Automatic properties provide you with a shorthand syntax for creating a property with a backing field. To learn more about automatic properties, “Data Access with LINQ to SQL.”

Declaring Constructors
A constructor is a special class method that is called automatically when you create a new instance of a class. Typically, you use the constructor to initialize private fields contained in the class. When creating a constructor in C#, you create a method with the same name as the class name.

You can create static constructors by using the static keyword when declaring a constructor. A static constructor is called once before any instance constructors.

Overloading Methods and Constructors
When a method is overloaded, a component contains two methods with exactly the same name. Many methods in the .NET Framework are overloaded, including the String.Replace() method, the Random.Next() method, and the Page.FindControl() method.

For example, here is a list of the three overloaded versions of the Random.Next() method:
  • Next()—Returns a random number between 0 and 2,147,483,647.
  • Next(upperbound)—Returns a number between 0 and the upper bound.
  • Next(lowerbound, upperbound)—Returns a number between the lower bound and the upper bound.
Because all three methods do the same thing—they all return a random number—it makes sense to overload the Next() method. The methods differ only in their signatures. A method signature consists of the order and type of parameters that a method accepts. For example, you can’t overload two methods that have exactly the same set of parameters (even if the names of the parameters differ). Overloading is useful when you want to associate related methods. Overloading is also useful when you want to provide default values for parameters.

When typing an overloaded method in Source view, the Intellisense pops up with all the different sets of parameters that you can use with the overloaded method.

Declaring Namespaces
A namespace enables you to group logically related classes. You are not required to provide a class with a namespace. To this point, all the components you have seen created have been members of the global namespace. However, several advantages result from grouping components into namespaces.

First, namespaces prevent naming collisions. If two companies produce a component with the same name, then namespaces provide you with a method of distinguishing the components.

Second, namespaces make it easier to understand the purpose of a class. If you group all your data access components into a DataAccess namespace and all your business logic components in a BusinessLogic namespace, then you can immediately understand the function of a particular class.

In an ASP.NET page, you import a namespace like this:
<%@ Import Namespace=”System.Collections” %>

In a C# component, on the hand, you import a namespace like this:
using System.Collections;

You can create your own custom namespaces and group your components into namespaces by using the namespace statement.

Creating Partial Classes
You can define a single component that spans multiple files by taking advantage of a feature of the .NET Framework called partial classes.

Partial classes are the basis for code-behind pages in the ASP.NET Framework. The code-behind file and the presentation page are two partial classes that get compiled into the same class.

Inheritance and Abstract Classes
When one class inherits from a second class, the inherited class automatically includes all the nonprivate methods and properties of its parent class. In other words, what’s true of the parent is true of the child, but not the other way around. Inheritance is used throughout the .NET Framework. For example, every ASP.NET page inherits from the base System.Web.UI.Page class. The only reason that you can use properties such as the IsPostback property in an ASP.NET page is that the page derives from the base Page class.

All classes in the .NET Framework derive from the base System.Object class. The Object class is the great-grandmother of every other class. This means that any methods or properties of the Object class, such as the ToString() method, are shared by all classes in the Framework. You can take advantage of inheritance when building your own components. You indicate that one class inherits from a second class when you declare a class.

Declaring Interfaces
An interface is a list of properties and methods that a class must implement. If a class implements an interface, then you know that the class includes all the properties and methods contained in the interface.

Interfaces are similar to abstract classes with two important differences. First, a component can inherit from only one class. On the other hand, a component can implement many different interfaces.

Second, an abstract class can contain application logic. You can add methods to an absract class that all derived classes inherit and can use. An interface, on the other hand, cannot contain any logic. An interface is nothing more than a list of methods and properties.

Using Access Modifiers
C# supports the following access modifiers, which you can use when declaring a class, method, or property:
  • Public—A public class, method, or property has no access restrictions.
  • Protected—A protected method or property can be accessed only within the class itself or a derived class.
  • Internal—An internal class, method, or property can be accessed only by a component within the same assembly (dll file). Because ASP.NET pages are compiled into different assemblies than the contents of the App_Code folder, you cannot access an internal member of a class outside of the App_Code folder.
  • Private—A private class, method, or property can be accessed only within the class itself.

Visual Basic .NET supports the following access modifiers (also called access levels), which you can use when declaring a class, method, or property:
  • Public—A Public class, method, or property has no access restrictions.
  • Protected—A Protected method or property can be accessed only within the class itself or a derived class.
  • Friend—A Friend class, method, or property can be accessed only by a component within the same assembly (dll file). Because ASP.NET pages are compiled into different assemblies than the contents of the App_Code folder, you cannot access a Friend member of a class outside of the App_Code folder.
  • Protected Friend—A Protected Friend method or property can be accessed within the class itself or a derived class, or any other class located in the same assembly.
  • Private—A Private class, method, or property can be accessed only within the class itself.
Intellisense and Components
Visual Web Developer automatically pops up with Intellisense when you type the names of classes, properties, or methods in Source view. You can add comments that appear in Intellisense to your custom components to make it easier for other developers to use your components.

You can generate an XML documentation file—a file that contains all the XML comments— for the components contained in a folder by using the /doc switch with the C# or Visual Basic command-line compiler. The command-line compiler is discussed in the second part of this chapter, “Building Component Libraries.”

Using ASP.NET Intrinsics in a Component
When you add code to an ASP.NET page, you are adding code to an instance of the Page class. The Page class exposes several ASP.NET intrinsic objects such as the Request, Response, Cache, Session, and Trace objects. If you want to use these objects within a component, then you need to do a little more work. Realize that when you create a component, you are not creating an ASP.NET component. In this chapter, we are creating .NET components, and a .NET component can be used by any type of .NET application, including a Console application or Windows Forms application.

Building Component Libraries
One of the advertised benefits of using components is code reuse. You write a method once, and then you never need to write the same method ever again. One problem with the components that have been created to this point is that they have all been application specific. In other words, you cannot reuse the components across multiple websites without copying all the source code from one App_Code folder to another. If you want to share components among multiple websites, then you can no longer take advantage of dynamic compilation. To share components, you need to compile the components explicitly in a separate assembly.

Compiling Component Libraries
You can use a number of methods to compile a set of components into an assembly:
  • Use the command-line compiler.
  • Use C# or Visual Basic Express.
  • Use the full version of Visual Studio 2008.

Using the C# Command-Line Compiler You can use the C# or Visual Basic command-line compiler to compile a source code file, or set of source code files, into an assembly. The C# compiler is located at the following path:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe

The Visual Basic command-line compiler is located at the following path:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\vbc.exe


If you have installed the .NET Framework SDK, then you can open the SDK Command Prompt from the Microsoft .NET Framework SDK program group. When the command prompt opens, the paths to the C# and Visual Basic .NET compiler are added to the environment automatically.

You can use the csc.exe tool to compile any C# source file like this:
csc /t:library SomeFile.cs

The /t (target) option causes the compiler to create a component library and not a Console or Windows application. When you execute this command, a new file named SomeFile.dll is created, which is the compiled assembly. As an alternative to compiling a single file, you can compile all the source code files in a folder (and every subfolder) like this:

csc /t:library /recurse:*.cs /out:MyLibrary.dll

The /recurse option causes the compiler to compile the contents of all the subfolders. The /out option provides a name for the resulting assembly.

Using Visual C# Express
You can download a trial edition of Visual C# Express from the MSDN website (http://msdn.microsoft.com). Visual C# Express enables you to build Windows applications, Console applications, and class libraries.

To create a class library that you can use with an ASP.NET application, you create a Class Library project in Visual C# Express (see Figure 15.7). When you build the project, a new assembly is created. If you need to use ASP.NET classes in your class library, such as the HttpContext class, then you need to add a reference to the System.Web.dll assembly to your Class Library project. Select the menu option Project, Add Reference and add the System.Web.dll from beneath the .NET tab

Using Visual Studio 2008
The easiest way to create a class library that you can share among multiple ASP.NET applications is to use the full version of Visual Studio 2008 instead of Visual Web Developer. Visual Studio 2008 was designed to enable you to easily build enterprise applications. Building class libraries is one of the features you get in Visual Studio 2008 that you don’t get in Visual Web Developer Express.

Visual Studio 2008 enables you to add multiple projects to a single solution. For example, you can add both an ASP.NET project and a Class Library project to the same solution. When you update the Class Library project, the ASP.NET project is updated automatically.

Adding a Reference to a Class Library
Now that you understand how you can create a class library in a separate assembly, you need to know how you can use this class library in another project. In other words, how do you use the components contained in an assembly within an ASP.NET page?

There are two ways to make an assembly available to an ASP.NET application. You can add the assembly to the application’s /Bin folder or you can add the assembly to the Global Assembly Cache. Adding an Assembly to the Bin Folder In general, the best way to use an assembly in an ASP.NET application is to add the assembly to the application’s root Bin folder. There is nothing magical about this folder. The ASP.NET Framework automatically checks this folder for any assemblies. If the folder contains an assembly, the assembly is referenced automatically by the ASP.NET application when it is compiled dynamically. If you are using Visual Web Developer, then you can select the menu option Website, Add
Reference to add a new assembly to your application’s Bin folder. Alternatively, you can simply copy an assembly into this folder.

Adding an Assembly to the Global Assembly Cache
All the assemblies that make up the .NET Framework class library are contained in the Global Assembly Cache. For example, the Random class is located in the System.dll assembly, and the System.dll assembly is contained in the Global Assembly Cache. Any assembly located in the Global Assembly Cache can be referenced by any application running on a server.

The Global Assembly Cache’s physical location is at the following path:
C:\WINDOWS\assembly

Before you can add an assembly to the Global Assembly Cache, you must add a strong name to the assembly. A strong name is similar to a GUID. You use a strong name to provide your assembly with a universally unique identifier.

Technically, a strong name consists of the name, version number, and culture of the assembly. A strong name also includes the public key from a public/private key pair. Finally, a strong name includes a hash of the assembly’s contents so that you know whether the assembly has been modified.

You can generate a strong name by using the sn.exe command-line tool like this:
sn.exe -k KeyPair.snk

Executing this command creates a new file named KeyPair.snk, which includes a new random public/private key pair.

Protect your key file. You should not reveal the private key to anyone. You can compile an assembly that includes a strong name by executing the Visual Basic

.NET command-line compiler like this:
csc /t:library /keyfile:KeyPair.snk /recurse:*.cs /out:MyLibrary.dll

The resulting assembly is strongly named with the public key from the KeyPair.snk file. The /keyfile option associates the key file with the assembly. In this case, the name of the resulting assembly is MyLibrary.dll.

An alternative method of associating a strong name with an assembly is to use the <Assembly: AssemblyKeyFile> attribute. You can add this attribute to any of the source files that get compiled into the assembly. For example, you can drop the file in

When using Visual C# Express or Visual Studio 2008, you can create a strong name automatically and associate the strong name with an assembly. Right-click the name of your project in the Solution Explorer window and select Properties. Next, select the Signing tab.

Creating the User Interface Layer
The User Interface layer is contained. Notice that the User Interface layer consists of a single ASP.NET page. This page contains no code whatsoever.

Creating the Business Logic Layer
The ASP.NET pages in your application should contain a minimum amount of code. All your application logic should be pushed into separate components contained in either the Business Logic or Data Access layers. Your ASP.NET pages should not communicate directly with the Data Access layer. Instead, the pages should call the methods contained in the Business Logic layer.

Creating the Data Access Layer
The Data Access layer contains all the specialized code for interacting with a database. The Data Access layer consists of the single component. (A real-world application might contain dozens or even hundreds of components in its Data Access layer.)

The SqlDataAccessLayer component has four public methods: ProductSelectAll(), ProductInsert(), ProductUpdate(), and ProductDelete(). These methods use the ADO.NET classes from the System.Data.SqlClient namespace to communicate with Microsoft SQL Server.

Notice that the SqlDataAccessLayer component is not completely isolated from the components in the Business Logic Layer. The ProductSelectAll() method builds a collection of Product objects, which the method returns to the Business Logic layer. You should strive to isolate each layer as much as possible. However, in some cases, you cannot completely avoid mixing objects from different layers.



No comments:

Post a Comment