Creating a Serviced Component

Now that you understand the advantages of using the features provided by Windows Component Services, you can create a .NET component that can take advantage of them. Here are the actions that are required when you want to use a .NET component in Windows Component Services:

  1. Add the appropriate code and attributes to your .NET component.

  2. Sign the component assembly with a strong name. Register the assembly in the Windows Registry.

  3. Configure the component in the Windows Component Services management console.

You will look at each of these steps in more detail in this section. This section will also include a discussion of how to design components for better performance and greater scalability. Finally, you will learn about transactions and how to control them by using attributes and code.

Adding Code and Attributes to Your Component

To create a new component that will be hosted by Component Services, you will typically create a new project in Visual Studio .NET by using the Class Library project template. Then you must use the Solution Explorer to set a reference and include an Imports statement in your code module for the System.EnterpriseServices namespace. This namespace includes two important classes: ServicedComponent and ContextUtil.

Each class in your component should be marked as Inherits ServicedComponent. You can also add a set of attributes to your class that determine how your class will use features of the ServicedComponent base class.

Listing 2.1 shows an example of the code as well as some of these assembly attributes. Table 2.1 lists some of the important attributes that are available. For a complete list, see the Microsoft Developer Network (MSDN) .NET Framework documentation.

Listing 2.1: Creating a Class for Use as a Serviced Component

start example
Imports System.EnterpriseServices <Assembly: ApplicationName("TransactionApp")>  <Assembly: ApplicationActivation(ActivationOption.Server)>  Public Class Account     Inherits ServicedComponent     Public Function Credit(ByVal accountNum as String, _         ByVal amount as Decimal) As Boolean         'working code goes here     End Function     Public Function Debit(ByVal accountNum as String, _         ByVal amount as Decimal) As Boolean         'working code goes here     End Function End Class
end example

Table 2.1: : ServicedComponent Attributes

Attribute Name

Scope

Description

General Attributes—Assembly Level

ApplicationActivation

Assembly

Library or Server. A library application runs in the same process with the code that calls it. A server application runs in its own process.

ApplicationID

Assembly

Enables you to identify your component by generating a Globally Unique Identifier (GUID) value.

ApplicationName

Assembly

Enables you to identify your component by a text name.

General Attributes—Class Level

ConstructionEnabled

Class

Enables you to pass a construction string that is supplied at runtime via the Component Services console.

JustInTimeActivation

Class

Enables your class to take advantage of COM+ just-in-time activation.

ObjectPooling

Class

Enables your class to take advantage of object pooling.

PrivateComponent

Class

Can be called only from code in the same application, not by external clients.

Synchronization

Class

Determines how COM+ manages concurrent access to your class. The valid settings are SynchronizationOption.Required (default), Disabled, NotSupported, RequiresNew and Supported.

Security Attributes

ComponentAccessControl

Class

Enables security checks to be performed before calling code in this class.

SecurityRole

Assembly, Class

Can be applied at assembly, class or interface scope. Use this attribute to name the role or roles that are allowed to call code in this component.

Transaction Attributes

Transaction

Class

Determines how your class participates in COM+ transactions. The valid settings are TransactionOption.Required (default), Disabled, NotSupported, RequiresNew, and Supported. The transaction attribute also has a TransactionIsolationLevel property (values: Any, ReadCommitted, ReadUncommitted, RepeatableRead, Serializable [default, the highest level], and a Timeout property that can be set in seconds—but if not specified, is infinite by default).

AutoComplete

Method

This attribute is applied at the individual method level. When code in this method completes successfully, the object automatically votes to commit the transaction. If an unhandled exception occurs in the method, the object automatically votes to abort the transaction.

The next code snippets show examples of using these attributes in your code to do the following:

  • Enable a construction string for the DataComponent class to be specified from the Windows Component Services console

  • Reduce overhead through use of object pooling for the BusyComponent class

  • Enable just-in-time activation to balance object activation time and overhead

    <ConstructionEnabled(True)> Public Class DataComponent     Inherits ServicedComponent     'add methods of the class here End Class <ObjectPooling(Enabled:=True, MinPoolSize:=10, MaxPoolSize:=20)> _    Public Class BusyComponent     Inherits ServicedComponent     'add methods of the class here    End Class <JustInTimeActivation(True)> _ <Synchronization(SynchronizationOption.Required)> _    Public Class ActiveComponent     Inherits ServicedComponent     'add methods of the class here    End Class

In addition to using attributes to make your class and its members, some methods of the ServicedComponent base class are commonly overridden to provide custom functionality for your component. These methods are described in Table 2.2.

Table 2.2: Methods of the ServicedComponent Class

Method

Description

Activate

This method is automatically called when the object is created or allocated from a pool. Used for custom initialization code.

CanBePooled

This method indicates whether the object is put back into the pool after being released by a caller Override this method to return true or false, as appropriate for your component.

Construct

This method can use the construction string value.

Deactivate

This method is automatically called when the object is about to be deactivated. Used for custom finalization code.

Dispose

This method releases the resources used by the serviced component.

Finalize

This method frees resources and perform cleanup before garbage collection.

Signing and Registering the Component Assembly

After you have finished developing your component, you need to prepare it for Component Services. The first step is to sign the assembly with a strong name. A strong name uniquely identifies an assembly by using a combination of the name, version number, and culture information, along with a public key and a digital signature.

The first step in strong-naming is to acquire a public key/private key pair. In a production environment, these keys, which are tied to your organization’s identity, will be protected. The responsibility for strong-naming code before deploying it to customers or users will fall to a few trusted individuals. During development and for learning purposes, however, you can use a tool that is provided with the .NET Framework to create key pairs. This tool is sn.exe.

To use sn.exe, you will need to go to the Visual Studio .NET command prompt by using the Windows menus. Go to Start Ø Programs Ø Microsoft Visual Studio .NET Ø Visual Studio .NET Tools Ø Visual Studio .NET Command Prompt. At the command prompt, navigate to the directory where your application resides. Give this command:

sn -k myKey.snk

This will create a file called myKey.snk that contains the key pair.

You will also have to add a new attribute to your assembly:

<Assembly: AssemblyKeyFile("myKey.snk")> 

The AssemblyKeyFile attribute was not included in Table 2.1 because it is a global .NET Framework attribute defined in the System.Reflection namespace. Any .NET assembly that requires strong-naming can use this attribute; it is not specific to only serviced components.

After you have created the key file, you can build your component and the resulting DLL will be strong-named.

The next step is to register the component. .NET Framework assemblies are designed to work without using the Windows system Registry, but because you want your component to interact with COM+, you must make a Registry entry for them. There are two ways to do this.

The first way is called lazy registration or dynamic registration; the component will register itself the first time it is called. The attributes that you included in your code provide enough information for proper registration. This technique is fine if you are still in development or if you expect only a single client application to use your component. The limitation of this technique is that any component that will be used by several different client applications and any component that is marked with an ApplicationActivation type of Server should be installed in the global assembly cache (GAC), which is a central directory on the computer that holds all shared components. For components that must be installed in the GAC, you must manually register the component and also manually install the component in the GAC.

Visual Studio .NET provides two more command-line tools to accomplish these tasks: gacutil.exe to install the component in the GAC and regsvcs.exe to register the component.

After you have completed these steps, you can use the Windows Component Services management console to view information about your component. Your will get an opportunity to practice these steps in Exercise 2.1.

Note 

You will learn more about strong-naming, key pairs, the GAC, and other deployment topics in Chapter 10.

Configuring the Component in Component Services

The final step is actually configuring the component in the Windows Component Services management console. You can access the Windows Component Services management console in Windows 2000 Server by choosing Start Ø Programs Ø Administrative Tools Ø Component Services. In Windows 2000 Professional and Windows XP, choose Start Ø Control Panel Ø Administrative Tools Ø Component Services. Figure 2.1 shows what the management console looks like.

click to expand
Figure 2.1: The Windows Component Services management console

After you drill down through the Treeview control to locate your application, as shown in Figure 2.1, you can right-click it to access the Properties dialog box. The properties will reflect the attribute settings that you made while you were coding the component.

In Exercise 2.1, you will use Visual Studio .NET to create a component and add references and attributes. Then you will create a public key/private key pair that will enable you to create a strong-named assembly when you compile the component. You will install the application into the GAC and register it for use with Windows Component Services. In Exercise 2.2, you will create a client application that uses the component.

Exercise 2.1: Creating a Serviced Component

start example
  1. Create a new Visual Studio .NET project by using the Class Library project template. Name this project AccountComponent.

  2. In the Solution Explorer, right-click the project name and choose Add Reference. In the Add Reference dialog box, select System.EnterpriseServices.

    click to expand

  3. Add an Imports statement to the top of the module: Imports System.EnterpriseServices.

  4. Create a class that inherits from the ServicedComponent base class, as shown in the following code. Include the assembly attributes as shown. The methods of the Account class do a simple calculation and return the result to the caller.

    Imports System.EnterpriseServices <Assembly: ApplicationName("TransactionApp")>  <Assembly: ApplicationActivation(ActivationOption.Server)>  Public Class Account : Inherits ServicedComponent     Private acctBalance As Decimal = 1000     Public Function Credit(ByVal accountNum As String, _         ByVal amount As Decimal) As Decimal            Return acctBalance + amount     End Function     Public Function Debit(ByVal accountNum As String, _         ByVal amount As Decimal) As Decimal           Return acctBalance - amount     End Function End Class
  5. Open a Visual Studio .NET command prompt and navigate to your project’s \bin directory. Use the strong name utility to generate a key pair. (The .snk file must be located in the same directory as the resulting DLL, or you will receive the error Error reading key when you build in step 7.)

    C:\path> sn.exe -k myKey.snk 
  6. Add an additional Imports statement and assembly attribute to your code to support strong-naming:

    Imports System.Reflection      <Assembly: AssemblyKeyFile("myKey.snk")>
  7. Build your component. Note: If you make changes to your component and need to build for a second time, make sure that you use the Rebuild Solution option on the Visual Studio .NET Build menu, or you might get an error when trying to use the component.

  8. Back at the Visual Studio .NET command prompt, you will need to install your component into the GAC and then register it for Component Services. Make sure you are in the directory containing AccountComponent.dll and then use gacutil.exe to install it into the GAC:

    c:\path> gacutil /i AccountComponent.dll
  9. Use regsvcs.exe to register your component:

    c:\path> regsvcs AccountComponent.dll
  10. You can now start the Component Services utility by choosing Start Ø Programs Ø Administrative Tools Ø Component Services. In the treeview list on the left side of the window, click Computers to expand it, then click your computer name, then click COM+ Applications, until you can see your component listed under the ApplicationName you specified in the assembly directive. Right-click the component icon and choose Properties from the pop-up menu.

  11. Save your project in Visual Studio .NET. You will be using it in future labs.

end example

Exercise 2.2: Creating a Client That Calls Methods of the Serviced Component

start example
  1. Start a new Visual Basic .NET Windows Application project. Name the project AccountTester.

  2. Set a reference to System.EnterpriseServices. You will also need to set a reference to the AccountComponent.dll, which you will need to browse to, under the .NET tab of the Add Reference dialog box. Be sure to select the component in the .NET tab, not the AccountComponent listed under the COM tab.

  3. Your project should look something like the form shown here. You will need a text box to input the amount to be credited or debited and someplace to display the account number and new balance. You will also need command buttons to execute the Credit and Debit operations.

  4. Add the statement Imports AccountComponent at the top of your code module.

  5. Add the following code to execute the Credit and Debit methods from the Account component:

    Private Sub btnCredit_Click(ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles btnCredit.Click       Dim objAccount As Account = New Account()       Dim newBalance As Decimal       Dim amount As Decimal       amount = CType(txtAmount.Text, Decimal)       newBalance = objAccount.Credit(txtAccountNumber.Text, amount)       txtNewBalance.Text = CType(newBalance, String) End Sub Private Sub btnDebit_Click(ByVal sender As System.Object, _    ByVal e As System.EventArgs) Handles btnDebit.Click       Dim objAccount As Account = New Account()       Dim newBalance As Decimal       Dim amount As Decimal       amount = CType(txtAmount.Text, Decimal)       newBalance = objAccount.Debit(txtAccountNumber.Text, amount)       txtNewBalance.Text = CType(newBalance, String) End Sub

end example

Now that you understand the basics of Serviced Components, in the following sections you will look at some additional topics, including designing components for performance and scalability, and using and managing transactions.

Designing Components for Performance and Scalability

When designing a component, you need to take a couple of considerations into account: performance and scalability. This is the primary reason for installing your components in Windows/COM+ Component Services. Just-in-time activation, which was introduced in Table 1.1, directly addresses these considerations.

Just-in-time-activation (JTA) is a feature that enables COM+ to activate an object instance very quickly when a client application makes a call on an object. When that method call is complete, COM+ can also quickly deactivate the object instance and release any memory or other resources that the object is holding. Other resources might be database connections, database locks, or open disk files. By releasing these resources quickly, they can be made available to other users.

JTA means that middle-tier components are not waiting for a client application to release resources in a timely fashion. There are many reasons why the client application might fail to do so—because the developer of the client application forgot to explicitly release the resource, because the end user of the application has not hit the Exit button, because the network connection was dropped, or any one of a dozen other reasons. Waiting for a client application to make decisions before releasing resources kills scalability.

JTA takes responsibility for managing this. As soon as each method call is completed, the object is deactivated. The memory and other resources that were being held by the object are released so that other user requests can be serviced. COM+ retains a certain amount of information about the object, so that if the client code makes another method call, the client will not get an error. COM+ will simply activate a new instance of the object so the method call will work.

There are two important things for the developer to remember here. First, because each method call is working with a newly activated instance of the object, any data from previous method calls is no longer available. This is referred to as a stateless model. There is no state, or persistent data values maintained from one method call to the next. Each method call to an object must be designed to pass all the data that is required for the object to complete its work. You cannot rely on the object “remembering” any data from previous method calls. Second, when you create components that will be used with Windows Component Services, you should remember that any code that must run when an object is activated or deactivated should be placed in the Activate and Deactivate methods as defined by the ServicedComponent base class. This is different from the .NET Framework standards of putting code into an object’s constructor (Sub New in Visual Basic .NET) and destructor (either Finalize or Dispose) methods.

Using and Managing Transactions

A transaction is a set of operations that all must successfully complete together. If any one of the steps fails, then the results of all steps must be rolled back, or cancelled. A classic example of a transaction is a procedure that transfers funds from one account to another. You would not want to debit the first account until you were certain that you could successfully credit the second account. .NET Enterprise Services offers the Distributed Transaction Coordinator (DTC) to manage transactions.

The DTC can manage transactions that involve multiple objects and even multiple components. The DTC uses two-phase commit to poll each object involved in the transaction to see whether it has completed its work successfully; this is phase 1. If any of the objects involved in the transaction encounter an error, their “vote” to commit the transaction is negative. After receiving “votes” from all the objects involved in the transaction, the DTC sends an instruction to all the objects to either commit or roll back their work; this is phase 2. If any one of the objects involved in the transaction voted to roll back, then all the objects must roll back their work.

start sidebar
ACID

Whenever transactions are discussed, you often hear the acronym ACID. The ACID properties describe important features of how transactions work.

Atomicity  All the work of the transaction is completed, or none of it is. This is the commit or roll back behavior discussed above.

Consistency The data used by the transaction must be in a state that meets all defined data integrity rules for the system when the transaction commits or rolls back.

Isolation  The data being used by the transaction cannot be seen by others until the transaction completes or rolls back.

Durability The work of the transaction must be saved permanently once completed.

end sidebar

In order for your component to participate in transactions that are managed by .NET Enterprise Services you must use the attributes provided by the ServicedComponent base class. As you can see in the next code snippet, each class is marked with a Transaction attribute. You must also set the TransactionOption value of this attribute to one of the allowable settings:

Required This is the default. This method must run in a transaction. If the code that called this method is already running in an existing transaction, then this method call will run as part of that transaction. If there is no existing transaction, then a new one will be started.

RequiresNew This method will always cause a new transaction to be started. This object will be considered the “root” object of the transaction.

Supported This method will run in a transaction if one already exists; otherwise, it will not require a transaction.

NotSupported This method will not run in a transaction.

Disabled The Transaction attribute is ignored.

Individual methods that will be used in automatic transactions can be marked with the AutoComplete attribute. When a method’s AutoComplete attribute is set to True, the method’s “vote” to commit or roll back the transaction will be set to Commit if the method completes successfully and set to Abort if an unhandled error occurs. This behavior will occur automatically, there is no need to add commit or rollback statements to your code.

The following code snippets show examples of using the Transaction and AutoComplete attributes in your code:

<Transaction(TransactionOption.Required)> Public Class Account     Inherits ServicedComponent     <AutoComplete(True)>Public Function Credit(ByVal accountNum as String, _         ByVal amount as Decimal) As Boolean         'working code goes here     End Function     <AutoComplete(True)>Public Function Debit(ByVal accountNum as String, _         ByVal amount as Decimal) As Boolean         'working code goes here     End Function End Class

The System.EnterpriseServices.ContextUtil class has properties that give you information about the status of the current transaction and has methods that you can use to affect transaction outcome. Every time a new transaction is started by .NET Enterprise Services, a new “context” for that transaction is also created and unique information about that transaction is available through the ContextUtil object.

Table 2.3 shows some of these properties, such as the ContextID, TransactionID, IsSecurityEnabled, and others. The ContextUtil class is a shared class, which means that you can call methods of the object without first explicitly instantiating it (this is similar to GlobalMultiUse classes in Visual Basic 6).

Table 2.3: Properties and Methods of the ContextUtil Class

Property or Method

Description

Public Properties

ActivityId

Gets a GUID representing the current activity

ApplicationId

Gets a GUID for the current application

ApplicationInstanceId

Gets a GUID for the current application instance

ContextId

Gets a GUID for the current transaction context

DeactivateOnReturn

Gets or sets the done bit

IsInTransaction

Indicates whether the object is running within a transaction

IsSecurityEnabled

Indicates whether the object has the Security attributes enabled

MyTransactionVote

Gets or sets the consistent bit

PartitionId

Gets a GUID for the current partition

Transaction

Returns an object that represents the DTC transaction

TransactionId

Gets the GUID of the DTC transaction

Public Methods

DisableCommit

Sets both the consistent bit and the done bit to False

EnableCommit

Sets the consistent bit to True and the done bit to False

GetNamedProperty

Returns a named property from the current context

IsCallerInRole

Indicates whether the identity of the user who called the method belongs to a specified security role

SetAbort

Sets the consistent bit to False and the done bit to True

SetComplete

Sets the consistent bit and the done bit to True

Earlier, you looked at how to use attributes to enable your objects to automatically vote on transaction outcome, simply based on whether a runtime error occurred during execution of the method. If you want an additional level of control over how your objects vote on transaction outcome, you can use methods of the ContextUtil class. These methods are SetComplete, SetAbort, DisableCommit, and EnableCommit. If you did any programming with MTS or COM+ components in earlier versions of Visual Basic, you will have seen these methods before. These four methods change the settings of important properties that determine what the final transaction outcome, either commit or abort, will be.

Each object participating in the transaction has two properties that show its status in regard to transaction outcome. These are frequently referred to as the done bit and the consistent bit. These are the terms that you will find in the Visual Studio .NET documentation, although the formal names of the properties of the ContextUtil class (as shown in Table 2.3) are DeactivateOnReturn and MyTransacationVote.

The DeactivateOnReturn property shows the current value for the done bit. If you call either the SetComplete or SetAbort method, it will have the effect of setting the done bit to True. You are indicating that, whether successful or not, your object has finished its work.

The MyTransactionVote property shows the current value for the consistent bit (this is sometimes also called the happy bit), which indicates whether your code has completed successfully. If you call the SetComplete method, the MyTransactionVote property will be set to True, and the SetAbort method will set the property to False. The SetComplete and SetAbort methods are straightforward and easy to understand.

There are two additional methods, EnableCommit and DisableCommit, which are a bit more complicated. As shown in Table 2.4, these two methods set the done bit to False. The objects are not deactivated at the end of the method call. These methods are typically used when the application design uses a root object, which in turn creates other objects that carry out the work of the transaction. When a secondary object returns from a method call with a status of DisableCommit, it is communicating to the root object that the original method call did not succeed, but control is returned to the root object to decide whether the transaction as a whole must be aborted or whether other actions can be taken to resolve the error situation. A status of EnableCommit indicates that the current method call was successful, but the object should remain activated so that the root object can make additional method calls.

Table 2.4: Methods Used to Control Transaction Outcome

Method

Done Bit

Consistent Bit

SetComplete

True

True

SetAbort

True

False

EnableCommit

False

True

DisableCommit

False

False

Listing 2.2 shows how to use SetComplete and SetAbort in code.

Listing 2.2: Calling the ContextUtil Methods

start example
<Transaction(TransactionOption.Required)> _    Public Function TransferToChecking(ByVal _    amount As Decimal) As Decimal       Try          'code here to debit savings account          'code here to credit checking account          'if successful          ContextUtil.SetComplete()       Catch          'if an error occurs          ContextUtil.SetAbort()       End Try End Function
end example



MCAD/MCSD(c) Visual Basic. NET XML Web Services and Server Components Study Guide
MCAD/MCSD: Visual Basic .NET XML Web Services and Server Components Study Guide
ISBN: 0782141935
EAN: 2147483647
Year: 2005
Pages: 153

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