The manifest contains the meta data required to specify: The assembly version The security information for the assembly The scope of the assembly Information to resolve refer
Trang 1instead of the original component When we execute the modified page, displayProducts_ovr.aspx, we can see that the
new value for average price is now displayed We will also add some code to display the newest product:
Transactions in NET
To provide integration with COM+, the CLR provides the same declarative transaction model This allows managed objects
to participate in existing transactions We can use these transactions from within ASP.NET pages, and also from within NET components
At the ASP.NET page level you can add a page-level directive for this:
<%@ Page Transaction="Required" %>
The allowable values are:
Disabled Transactional context will be ignored This is the default
NotSupported The page does not run within a transaction The object context is created without a transaction
Supported The page runs within the scope of an existing transaction, if one is active If no transaction scope is
active, the page runs without a transaction
Required The page runs within the scope of a transaction, either an existing one, or creating one if no existing
transaction exists
RequiresNew The page requires a new transaction, and one will be created for each page request
To participate in the transaction success or failure, you can either rely on transaction AutoComplete or explicitly commit
or abort the transaction With AutoComplete enabled, your page will vote to commit the transaction if the page runs without throwing an exception AutoComplete is enabled by default on a page, so if the page completes successfully, then the page will vote to commit
You can also explicitly control the transaction result by calling the methods of the ContextUtil class To vote to abort
Trang 2a transaction, you call:
A serviced component has its configuration information held by the COM+ catalog When you use a serviced component within your application, COM+ will create a context service layer based on the attributes you have assigned to your serviced component Basically, the context service layer runs within COM+ Services, and communicates with your serviced components, which are still running within NET
There are several steps to go through to create serviced components:
Derive your class from the System.EnterpriseServices.ServicedComponent class
Add the TransactionAttribute to your class
Create a strong name for the assembly
Add assembly attributes to identify the strong name, and the COM+ application name
Register the assembly with the COM+ catalog This can be manually done, or under certain circumstances, done automatically (this is called lazy registration)
Creating a Serviced Component Class
Creating the class and adding the attributes is simple Here is an example written in Visual Basic NET:
Option Explicit
Trang 3Public Sub Transfer(FromAC As String, ToAC As String, Amt As Decimal)
Dim f As String = "Transferring " & Amt.ToString() & " from " _
& FromAC & " to " & ToAC
Trang 4<assembly:ApplicationName("DotNetBank")>
The AssemblyKeyFile attribute is used to indicate the file that contains the public and private keys for the strong name for this assembly Later in this chapter, we will look at strong-named assemblies These types of assemblies are required
if we want to have multiple versions of an assembly executable at the same time on the same system The
ApplicationName attribute defines the COM+ application that this NET component will execute within If the application is not found, then COM+ will create an application with the name supplied- in this case DotNetBank
<Transaction(TransactionOption.RequiresNew)>
The other attribute that has been included with this component is the Transaction attribute This attribute specifies the transaction parameter for this component In this case, we want to make sure that the component executes inside a new transaction The parameter that we pass is an enum defined as TransactionOption.RequiresNew
The C# version of the serviced component is nearly identical to the Visual Basic NET version The only difference is the syntax that is used to denote the attributes within the code Whereas in the Visual Basic NET version, we used < > to delineate the attributes, the C# version uses [ ] to indicate the attributes:
Trang 5string f = "Transferring " + Amt.ToString() + " from " +
Additional Assembly Attributes
As well as the ApplicationName attribute, you can also add COM+ component details, such as:
ApplicationActivation, to determine how the assembly is activated (Library or Server)
ApplicationID, to specify a GUID
Description, to give the assembly a description
For example, in Visual Basic NET these would be:
<assembly:ApplicationActivation(ActivationOption.Library)>
<assembly:ApplicationID("guid")>
<assembly:Description(".NET bank assembly")>
Accessing COM+ Context
To access the COM+ context from the NET serviced component, you use the
System.EnterpriseServices.ContextUtil class There are no major performance penalties since the cost to transition into COM+ context is very small The context (including transactions) flows with the call, so transactions automatically flow
Trang 6Registering the Serviced Component
Classes using COM+ services must be registered, which can be done using the Register Services Tool The syntax is:
regsvcs [Options] AssemblyName
where Options can be one of:
/fc Find or create a target application This is a default
/c Create the target application, generating an error if it already exists
/exapp Expect an existing application
/tlb:tlbfile Filename for the exported type library
/appname:name Use the specified name for the application
/parname:name Use the specified name or ID for the target partition
/extlb Use an existing type library
/reconfig Reconfigure the existing target application This is a default
/noreconfig Do not reconfigure the existing target application
/u Uninstall the target application
/componly Configure only the components, not methods or interfaces
/nologo Suppress logo output
/quiet Suppress logo and success output
Using this utility performs the following actions:
Loads the assembly
Registers the assembly
Generates a type library
Registers the type library
Installs the type library into the COM+ application
Configures the COM+ application
For example, to register the bank assemblies:
Trang 7If you then examine the COM+ Component Services, you'll see the NET components
These can be treated like any other transactional COM+ component
Lazy Registration
If a serviced component is used from managed code, and it is not already registered as part of a COM+ application, then the registration and configuration is performed automatically for you This may seem like an ideal solution, but it does require administrative privileges, so is not suitable for all scenarios By and large, it is best to manually register components, perhaps as part of the installation
Trang 8Serviced Component Security
Integration with COM+ security is also provided as part of serviced components, allowing access to the
SecurityContext object of the COM+ application For this to succeed the managed code needs to obtain an NT security token and perform impersonation before calling the NET object
What are Assemblies?
There is no concept of an assembly as a file- there is no such thing as a assembly file that we have to worry about now
The assembly is a collection of files that together make up the assembly The only requirement is that these files need to all reside in the same directory on the disk When they are placed into an assembly, the CLR treats all these as a single unit
As you can see from the above figure, the manifest is what is used to describe the contents of the application The manifest can either be embedded within a single DLL, as seen in the single file assembly, or can be in a separate file, as
is the case with the multi-file assembly When we talk about DLLs in NET, they are just a bit different from what they were
in COM or Win32 While the extension is the same, they are executed by the CLR, instead of being executed as native code
Trang 9The assembly can be thought of as a logical DLL In the past, we would distribute components or resources through the deployment of a DLL Now, we can distribute the pieces of an assembly in the same way The main difference is that the DLL needed some other information somewhere- usually in the registry- to tell the system that it was there ready to run With an assembly, it carries that information along with it, in its meta data
An assembly is not an application An application is built from one or more assemblies The assemblies that make up an application can be deployed in a number of different ways Since an assembly contains its own meta data, it is capable of telling the operating system all about itself It does not rely on entries into the registry to describe itself
This association of meta data with executable code simplifies the distribution of an application All you need to do to deploy an application is simply to copy all of the assemblies that make up the application to a directory on the disk This
is known as XCOPY deployment, since the only tool required to deploy the files to disk is the XCOPY console command When the application is first executed, the meta data within the assemblies tells the system all that it needs to know in order to execute the application As we saw earlier, this may not always be the case If you are using serviced components, these need to be registered with COM+
You can also use more traditional installation mechanisms to distribute applications built out of assemblies You can build
an .msi file and use the Windows Installer to deploy the files into the correct location Likewise, you can build a CAB file
and have a browser download the file to the system and then execute the application In either case, all that the installation mechanism is responsible for is getting the proper files into the proper location on the destination system- no information about the assemblies needs to be added to the registry of the target system
Assemblies and Versioning
There are currently two problems with the Win32 architecture that have combined to give us what is known as DLL Hell
In DLL Hell, there is no control entity that is responsible for all of the DLL files installed onto a system Information about
a COM DLL is held in the registry It can be easily overwritten by another application For DLLs that aren't COM DLLs, there
is no entry whatsoever in the registry An application install program can also overwrite an existing DLL This can play havoc with any existing application that was relying on that particular DLL performing a specific function when a method
is called
One of the specific problems with Win32 is that there is no system-level enforcement of versioning rules between components It is left up to 'best practices' coding, which says that once an interface is published it can never be changed, but there is nothing in the operating system that explicitly prevents this The other problem is that there is no common way for an application to say that it needs version 1.2.1.1234 of a particular component It is left up to the developer to check the version of a DLL before calling into it If that check is not done, and the application finds a different version, then the code that it is relying on may no longer be there, or it may not perform the function that it expects, even if the interface is still intact To attempt to combat this, Windows 2000 added System File Protection This is an OS feature that can stop any installation program from overwriting any system DLLs
The CLR extends this support by allowing developers to specify the specific version of a component that they want their application to use It provides all of the support to make sure that the proper version is located and used for the requesting
Trang 10application In doing this, it also allows for the execution of code from two similar components, only differing in version This is known as side-by-side execution and we will look at that a bit later in the chapter
Assembly Manifest
In order for an assembly to describe itself to the CLR, it must contain a set of meta data This meta data is contained in the assembly's manifest The manifest contains the meta data required to specify:
The assembly version
The security information for the assembly
The scope of the assembly
Information to resolve references to the resources and classes of the assembly
The manifest for an assembly can either be stored in an EXE or DLL file, or in a standalone file Remember that the assembly can be made up of one or more files- in which case there isn't a specific file that contains the entire assembly
In a single file assembly, the manifest is part of the DLL or EXE file that is the assembly
The manifest of an assembly lists all of the files and resources that make up the assembly It also lists all of the classes and types defined in the assembly, and specifies which resources or files within the assembly map to which classes or types The manifest also identifies any other assemblies on which it is dependent
In creating a multi-file assembly, the Assembly Generation tool (AL.EXE) is used to create the manifest for the assembly
To create a multi-file assembly, you compile the individual source files without an assembly Then the AL tool is used to read through the compiled modules and create an assembly for the full set of modules
Meta data
The manifest will specifically contain these pieces of meta data:
Assembly Name - This is a textual string name that identifies the assembly When an assembly is used by one application, the developer can generally enforce a unique name for each assembly, thus preventing name collisions However, when an assembly is designed to be shared, a more unique naming method must be used This is called a strong name, and creating one allows the assembly to be stored in the global assembly cache The global assembly cache is used to store assemblies that can be used by several applications on a machine
To store an assembly into the global assembly cache, there are three steps that have to be followed:
First, you must create a strong name for the assembly using the SN.EXE tool This tool will generate a file that contains the necessary public and private keys to define a strong name
Second, the contents of that file are passed to the Assembly Generation Tool (AL.EXE) to create an assembly with a
Trang 11strong name associated with it
Finally, to install the assembly into the global assembly cache, you will use the Global Assembly Cache Tool
(GACUTIL.EXE) This is a tool that is used to manipulate the contents of the global assembly cache, including adding components to the GAC
Version Information - The components of the version number are the major and minor version numbers, a build number, and a revision number This is represented as a set of four numbers with the format:
<major version>.<minor version>.<build number>.<revision>
When the CLR is checking to see if an assembly is the proper version, it first checks the major and minor version numbers These must match in order for the assembly to be compatible If these two numbers match but the build number is different, then as long as the build number of the assembly is greater than the build required by the application, it can be assumed to be backwards-compatible with the version expected by the application
Assembly File List - Lists each file contained in the assembly, along with the relative path to the file For version
1 of the NET Framework, all files in an assembly must be in the same directory as the manifest file This only holds true for a multi-file assembly
Type reference information - Maps all of the types included in the assembly to the specific file in the assembly that contains the type This is necessary so that any types referenced within the classes contained in the assembly can be resolved by the runtime
Referenced assemblies - Lists all of the other assemblies that are statically referenced within the types contained
in this assembly Each entry contains the name of the assembly along with the required version information
There is also custom meta data that can be included by the developer Only the developer can use this information - the CLR does not use this information in any way In the first release of the NET architecture, there are two sets of custom assembly meta data The first set is made up of nine classes from the System.Reflection namespace You can use this namespace to query the values for this meta data at run-time System.Reflection meta data includes:
Company information
Build information, such as 'Retail' or 'Debug'
Copyright information
Additional naming and version information
Assembly Title and Description
Product and Trademark Information
The second set is made up of classes from the System.Runtime.CompilerServices namespace This meta data includes:
Trang 12 Cultures or spoken languages supported by the assembly, not programming languages.
Operating systems and processors the assembly has been built to support Version 1 of the CLR does not use this information
Since the components within an assembly are so thoroughly defined by the meta data, you can even define a new class that inherits from an existing class directly from the compiled code - you don't need to access the sourcecode to do this
In fact, the components do not even need to be in the same language, as long as both are managed components We saw
an example of this earlier
The assemblies and the components within them are said to be self-describing This means that they carry all the information that other components need to know in order to interact with them This information is all carried within the meta data of the assembly There are no more IDL files in the NET architecture or public header files that get out of sync with the executables, and you can always be sure that the meta data information being used by the runtime is the proper meta data for the code being executed, since they are held together in the assembly
Side-by-Side Execution
The ability to run multiple versions of the same component at the same time is a very valuable feature of the CLR It can even execute two versions of the same component within the same process The ability to do this is called side-by-side execution By allowing this, the NET architecture offers the developer an advantage over architectures such as COM While there have been ways in the past to do side-by-side execution, the implementation of it in NET frees the developer from most of the worries associated with doing it By handling it in the plumbing of the CLR, the developer only has to worry about the business-specific code in their application
When creating new versions of a component, a developer doesn't have to worry as much about maintaining compatibility with previous versions Since the older component can run right alongside the new component, and the application using the component knows which version of the component to use, both can co-exist peacefully on the same machine There are some precautions that the developer must take into account when having components that will run side-by-side with previous versions For example, if the component is relying on a physical file as a data cache, then two components executing side-by-side will try to access the same file The components would need to be written such that they keep the file in a location that is dependent on the version of the component being executed
Trang 13Summary
The flexibility and power of the NET architecture extends beyond just creating applications, web services, and ASP.NET pages We can create powerful business components using the NET Framework and the CLR that have just as much power and flexibility as COM objects In some cases, the capabilities offered by the CLR provide distinct advantages over COM Now we can actually derive new objects from classes written in different languages And we can do this even if all
we have is the compiled version of the base class However, that doesn't mean that we can't have COM and NET work together in the same application
In this chapter, we have specifically looked at:
How to write a business object in two different languages, and then use the same test program to work with both objects
How to take a class written in one language and extend its functionality by deriving a new class in a totally different language
How to make use of transactions in your ASP.NET pages and within your NET components
How assemblies and meta data provide a way to package the executable code along with detailed descriptions of the code that is executing
Trang 14Building ASP.NET Server Controls
Since the early days, COM developers around the world have been building reusable visual controls In the early 90's, building such controls required significant time and investment in understanding the myriad of COM interfaces that a control had to implement and use
Later in the decade, C/C++ frameworks like the Microsoft Foundation Classes (MFC) and the Active Template Library (ATL) made control development a little easier They provided reusable classes and templates that implemented a lot of plumbing code required for controls But the true breakthrough in popular control development didn't really happen until
1996 when the release of the Visual Basic Control Creation edition made control creation far simpler, and consequently very popular globally
The upside of control development, and the reason why people still write so many controls today, is reusability Once you have conquered visual control development, the reward of being able to write a control once (the grid control being the canonical example) and then reuse it in numerous control containers (such as Word, Excel, etc.) makes it worth all the pain You can, of course, reuse the control in your own suite of products, not to mention the fact that you could make a good living selling them to other developers These same advantages apply in the world of ASP.NET controls, but there is one big difference: ASP.NET controls are easy to get started with
ASP.NET allows you to build reusable visual controls that can render themselves as HTML or any other mark-up language such as WML Many high level similarities can be drawn between COM controls and ASP.NET controls, as they both enable reuse within UIs But in reality, they are very different in the way they are implemented ASP.NET provides a clean and easy-to-use class hierarchy for implementing controls, and there are no esoteric interfaces or threading models that are difficult to understand ASP.NET still uses interfaces, but there really aren't more than a couple you'll use on a regular basis
In this chapter we're going to look at ASP.NET control development covering:
When, why, and how you can build ASP.NET controls in both Visual Basic NET and C#
How controls form the basis of all page rendering in ASP.NET
How controls persist state across page invocation
How controls interact with postback and can raise events
Trang 15 Building controls that, themselves, use other controls to render their UI
Writing a Simple Control
To demonstrate the basic principles of control development and to show how easy ASP.NET control development is, we'll kick off this chapter by writing a simple Label control, which can render a text message We'll initially develop the control
in C#, review the code, and then rewrite it using VB This example will hopefully prove to you that control development really isn't that difficult, and it is, at the very least, worth understanding how controls are written so you can better understand how ASP.NET renders pages
Trang 16it is good practice to include it
System.Web.UI - contains the ASP.NET control classes, many of which are divided up into namespaces based upon their family (System.Web.UI.WebControls, System.Web.UI.HtmlControls, etc.) This reference
is needed as we are using the Control and HtmlTextWriter classes
The next line of the code marks the beginning scope of a new namespace we have chosen for our control called
WroxControls:
namespace WroxControls
{
Anything declared within the next section of the code until the end of the namespace seven lines later is a member of the
WroxControls namespace This means that the full namespace-qualified reference to our control is
WroxControls.MyFirstControl Any code using our class must use this full name unless the Using directive is included to import all definitions within our namespace into its default scope
The next few lines of code declare the class for our server control:
public class MyFirstControl : Control
{
protected override void Render(HtmlTextWriter writer)
Trang 17{
writer.Write("<h1>ASP.NET Control Development in C#</h1>");
}
}
The class is called MyFirstControl It's derived from the class Control, (which is part of the System.Web.UI
namespace) declared as public, and has one protected method called Render The Control class implements the basic plumbing code required by a server control to be used within an ASP.NET page
The name of the class is arbitrary and you can call your controls whatever you like, as they are defined in your own namespace You should avoid using names that are already used by ASP.NET (or any other part of the runtime), as name conflicts will occur if two namespaces are imported using the @Import directive by a page, or the using directive inside
of a source file If you decided to create your own label control do not call it Label; call it YourLabel or some other meaningful name such as WroxLabel
By marking our class as public, we are stating that anybody can create an instance of our class and use it If we specify that it is private (by omitting the public attribute), the class will only be usable by other classes within the same namespace If an ASP.NET page attempts to access a private class, the runtime will raise an error saying the class/type
is not defined This is somewhat confusing when trying to track down an error, so it is always worth checking the access control of a class as it is easy to forget to add the public keyword when developing
The Render method is declared using the override keyword This keyword tells the compiler that the base class
Control implements the Render member, but our class wants to override the method so that it can control the output created at run-time itself The implementation of this method calls the Write method of the HtmlTextWriter object, which results in the HTML "<h1>ASP.NET Control Development in C#</h1>" being written to the page output stream
Once you have created a server control and compiled it (as we'll demonstrate shortly), you can use it in an ASP.NET page
To do this, you use the @Register page directive to associate a tag prefix with a namespace containing a server control, and then use the tag prefix plus the class name of a server control, as shown here:
<%@ Register TagPrefix="Wrox" Namespace="WroxControls" Assembly="MyFirstControl" %>
<html>
<body>
Trang 18<Wrox:MyFirstControl runat="server" />
</body>
</html>
Create a new file called myfirstcontrol.aspx to contain this ASP.NET page
This page tells the ASP.NET page compiler that when it sees any element starting with 'Wrox:' it should search the namespace WroxControls for a class whose name matches the element name (that is, MyFirstControl)
Furthermore, the assembly attribute defines the physical assembly in which the class is contained At run-time, assuming ASP.NET located the class, it creates an instance of it, and calls its Render method to allow it to write data to the output stream being created (for example, the page)
The position of the HTML that a control creates in the final page depends upon where that control is declared inside the ASP.NET page code In this page we'd expect the server controls output to be rendered after the body tag
In this example, once our control has inserted its HTML into the output stream, the final page sent down to the client browser will look like this:
Trang 19To test the ASP.NET page yourself and view the output, you need to compile the C# sourcecode to create an assembly This is placed in the bin directory of the application on your web server, causing the ASP.NET runtime to scan the assembly and make an internal note of the namespaces and classes/controls within it
Compiling the C# Control and Creating an Assembly
To compile our server control class, bring up your text editor, enter the following batch commands, and save the file as
make.bat:
set outdir= \bin\MyFirstControl.dll
set assemblies=System.dll,System.Web.dll
csc /t:library /out:%outdir% /r:%assemblies% MyFirstControl.cs
For the class to compile correctly, you'll need to change the outdir parameter ' ', in line one, to match a directory on your machine that is configured as a virtual directory
The first two lines of the make.bat file declare a couple of variables These make the line that actually does the compilation more readable The compiler options are discussed in Chapter 3 The important points to note about this file are:
A reference to the System.dll and System.Web.dll assemblies is added using the /r parameter We have
to do this so that the compiler knows where to search for the namespaces/classes we're importing into our control with the using directive If you do not add a reference to the assembly containing referenced classes you'll get a compile error
The output of the compiler (MyFirstControl.dll) is placed directly into our web application's bin directory When ASP.NET has to create a class at runtime, this directory is searched Using the meta data contained in our assembly, ASP.NET will know where to locate the WroxControls namespace specified in the page's
Trang 20@Register directive By compiling our control directory to the bin directory, we are less likely to forget to copy the assembly
Run the make.bat file, and you'll see some output like this:
If all goes well, check that the bin directory contains the compiled assembly If it exists, you should be able to open the ASP.NET page we created earlier and see the output of the control
First Control Complete
Although the C# control we've just developed is about as simple as it gets, and doesn't really have any practical use as yet, you've just seen the full development cycle required for a simple ASP.NET control Hopefully, you now realize that: (a) control development is nothing like it was in the COM days, and (b) maybe control development is worth checking out
in more detail Things will get more advanced from here on in, but all of the code is clean and fairly easy to understand once you've grasped the basic concepts that we've been discussing
To show that control development in Visual Basic is just as painless, we'll create an almost identical control called
MyFirstControlInVB
Control Development in Visual Basic
One of the great things about Visual Basic NET is that the language has arrived in the 21st Century with a bang It now has all the object-oriented (OO) features that make it a first-class programming language However, as you'd expect, the
VB team and C# teams at Microsoft are competing with each other for adopters, and as a result they have used different names for certain attributes and directives within their respective languages that they feel are more suitable for their audience of programmers This is fair enough, but it does means that switching between the languages can be a little confusing at first