Team LiB
Previous Section Next Section

.NET Enterprise Services

.NET Enterprise Services expose all the functionality you know from COM+. .NET Enterprise Services makes it possible for your enterprise applications to take advantage of object pooling, data connection pooling, and other functions found in COM+. Many developers avoid using.NET Enterprise Services, since.NET Enterprise Services today uses COM+ (see Figure 5-14), which is actually built on the unmanaged code, thereby resulting in slight overhead when used. Interoperability gives you this overhead, no doubt about it, but if you compare this to creating the pooling by yourself and also handling the transaction support, this overhead is a small sacrifice for the smaller amount of code generated and ease of maintenance. The next version of the Component Services will hopefully run as managed code, so that the overhead associated with Component Services disappears. We saw the development and performance boost that happened when MTS was integrated closer to the core and renamed COM+; we hope a similar boost will occur when Component Services is rewritten in .NET and the .NET Framework is more closely integrated with the system. As you will see in Chapter 9 in this book, the move from Windows 2000 Server with .NET Framework to Windows Server 2003 boosts the performance dramatically.

Click To expand
Figure 5-14: An n-tier architecture using Enterprise Services with COM+

In order to use .NET Services from your application, your classes need to derive from ServicedComponent and use various custom attributes (the common way in .NET to customize the behavior of your classes and methods) to specify the actual services required. Here we will discuss the major benefits for using .NET Enterprise Services and show how you can improve scalability and reliability by using it in your enterprise architecture.

Tip 

Try keeping the serviced components as library applications, since the performance hit that results from running them in a separate context as a server application may sometimes be too high. When you need to run the components on remote machines, or want to run them under specific security context, you need to run them as server applications.

Transactions

Transaction support is only one of the benefits of using .NET Enterprise Services. Other benefits are object pooling, role support, and queued components. Enterprise applications work with sensitive data, and therefore transaction support is of great importance. Without transactions, data will soon or later be corrupted when a cascade update or change to the data server is interrupted by mistake. By using the transaction support found in .NET Enterprise Services, you do not need to develop the transaction support yourself, thereby saving time in the development phase and also during the maintenance phase because the code will become less complex.

Assume that you have an enterprise application that deals with bank accounts and their information. Transactions between bank accounts need to be handled to avoid loss of information. A simple server class in your application may look like this:

using System.EnterpriseServices;
[assembly: ApplicationName("BankComponent")]
[assembly: AssemblyKeyFileAttribute("Demos.snk")]
namespace BankComponent
{
      [ Transaction(TransactionOption.Required)]
      public class Account : ServicedComponent
      {
      [ AutoComplete]
            public bool Post(int accountNum, double amount)
            {
            // Updates the database, no need to call SetComplete.
            // Calls SetComplete automatically if no exception is thrown.
            }
      }
}

To make this component work, you first need to reference System.EnterpriseServices. Secondly, the Account class needs to derive from the ServicedComponent in the EnterpriseServices. The configuration of the COM+ application that will host the managed code can be controlled via attributes. The first attribute here is the attribute on the class ([Transaction (TransactionOption.Required)]), which specifies that a transaction is required for this class. The [AutoComplete] attribute tells you that the function Post will auto-commit the changes when the function exists (there is no need to explicit call setComplete). If an exception is thrown in the function, the transaction is automatically rolled back. The ease of implementing the transaction support is obvious—you do not need your own transaction code; that makes the application easier to read and to maintain.

The client that calls your server class looks like Listing 5-1.

Listing 5-1: Client Class Calling Your Server
Start example
BankComponent Client
using system;
using BankComponent;
namespace BankComponentClient
{
    class Client
    {
        public static int Main ()
        {
            Account act = new Account();
            act.Post(6, 90);
            act.Dispose();
            return 0;
        }
    }
}
End example

The client looks like any normal client, and the call to your server component account is like any normal object call in .NET. A reference to the BankComponent namespace, the System.EnterpriseServices assembly, and the final call of the Dispose method is a general .NET requirement when a derived class does not override all the base class methods (see Figures 5-15 and 5-16).

Click To expand
Figure 5-15: The general context for an unmanaged application and its components
Click To expand
Figure 5-16: The context for the example managed application

Deployment

So far you have a server component and a client component. The next step is to deploy your server component on the server, so you can access it from the client. In a traditional COM+ world, you would have registered the component by yourself in Component Services. In .NET, you can do this in three different ways—the manual way, which we recommend for production environments to control the installation process, the dynamic way, and the programmatic registration way.

Note 

If the components are to be accessible from old COM components, dynamic registration cannot be used. Dynamic registration can only be used when you have a managed client.

When deploying the dynamic way, the COM+ application that hosts your managed components is configured based on the custom attributes in the code. With custom attributes, you can specify what services are required, such as the transaction attribute in your server class. The attributes are stored in the assembly metadata. When the components are executed for the first time and you want them installed in Component Services, the metadata and the attributes are read by using EnterpriseServices.RegistrationHelper (see Figure 5-17). In order to make the process of registration easier, all forms of registration use the component EnterpriseServices.RegistrationHelper. This component is accessible as a managed class as well as a COM+ object.

Click To expand
Figure 5-17: The calls that occur when your server component is executed the first time and registered in Component Services

The workings of the RegistrationHelper class are quite detailed (and for a more detailed explanation of what is going on behind the scenes, please visit http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/entserv.asp). But we want to mention some things that might otherwise confuse you when working with Enterprise Services.

Your .NET classes are registered by the RegistrationHelper and appear on the machine as COM components. If your components do not implement any extra interfaces beside the default interface, the method names will not show up when browsing the components via the Component Services Tool. To be able to see the methods that is in the standard interface, you need to add the ClassInterfaceAttribute to all methods.

When the RegistrationHelper registers your .NET classes as COM components, it generates GUIDS for the COM components. If the assembly is not signed, the GUIDs are generated based only on type and namespace names. This can result in nonunique GUIDs.

Note 

Assemblies that use COM+ services must be signed. Registration will fail if assemblies are not signed.

You should also verify that you are not using the ApplicationID attribute in your assembly. If you are using it, your components will not install into different COM+ applications to allow different configuration data for the components. COM+ requires unique application IDs.

As you already know, dependency files are installed either in the same directory as the client or in the GAC.

Note 

Assemblies that use serviced components in COM+ server apps should be placed in the GAC. Assemblies that use serviced components in COM+ library apps may not need to be placed in the GAC (unless they are located in different directories). The only exception is when hosting with ASP.NET, assemblies should not be placed in the GAC to enable shadow copy to operate correctly. To remove a .NET application that uses serviced components, remove the assembly from the GAC (if it has been registered with the GAC), deregister the assembly from COM+ using regsvcs.exe, and then delete the assembly and the associated type libraries. Assemblies that are dynamically registered do not need to be placed in the GAC.

Next the RegistrationHelper tries to find out if you have already installed a previous version of the component in a COM+ application. A new COM+ application is created if no existing application is found.

The last step the RegistrationHelper does is to read all your attributes on the classes and methods in the component and apply them to the installed component in COM+.

Since the RegistrationHelper accesses the registry and adds new COM+ applications to Component Services, the user who is executing the RegistrationHelper needs to have administration rights on the machine. This is a common mistake when using Enterprise Services from an ASP.NET-based application. The components are initialized from the ASP.NET account, so dynamic registration of the components will fail because this account does not have the right permissions by default. We always recommend to do the registration and creation of the COM+ application manually to have control of the process.

Note 

Since attributes are coded by the programmer in the class, these attributes might be in conflict with each other. Some attributes cannot be listed together with other attributes. These mismatches will not be detected until the RegistrationHelper tries to apply the attributes to the registered component. To avoid these errors, always make it a habit to register your components manually to catch these attribute mistakes before going live.

If no attributes are found in the class, the default values are used for the component in the COM+ application. For instance, if a component is registered and does not have a Transaction attribute specified, the unconfigured default value for the transaction setting in the catalog is set to TransactionOption.Disabled.

When the component has been registered, some configuration parameters are possible to change such as object pooling, timeouts, and JIT; however, changing some of these parameters can prevent your code from running correctly. For instance, disabling the object construction for a component that requires to get a constructor string will probably result in an error.

Although some configuration parameters are configurable through the Component Services Tool, others are only read from the class itself and not from the configuration in Component Services. These are JIT, AutoComplete, and object pooling (except for the size of the pool that is configurable through Component Services). Changing these parameters for the component via the Component Services Tool doesn't affect the component at all.

Versioning

Anyone who has ever worked with the Windows DNA environment knows that versioning can be a huge problem and a nightmare—especially when users call you and say that the application does not work after the version 1.1 release. .NET removes the reference problems known as DLL Hell and helps you to manage your different versions of the application in a more convenient way. In .NET, you can isolate a specific version of an application, because all dependent files can be put into the application directory together with the version and not necessary registered globally on the machine. This makes it possible to have many different versions of the same application installed on the machine and running concurrently.

To be able to install a package into COM+, the package must have a unique ID. It is possible to generate a unique ID by applying a GUID in your server class. However, it is better to use versioning of your package instead of applying new GUIDs. As always, you need to increment your version number on the assembly as soon as you change anything in the application. When the incremented assembly is installed in COM+, COM+ generates a new GUID for it, and installs it parallel with the old version in the same COM+ application. By default, the Visual Studio Environment adds the attribute [assembly: AssemblyVersion("1.0.*")], which will increase the version number of the assembly for every build.

Tip 

We advise you to change the * in the AssemblyVersion attribute to the current minor version you are working on, and manually increment the number when you are going to release a new version to the server. This will give you better control over when new minor release numbers are generated as well as prevent you from accidentally incrementing the minor version just because you test-built the assembly.

You may be wondering what happens when you install a new version—will the old clients stop working? No, the old clients will still work and bind to the old assembly by using the CLR version policy. The version policy differs depending on whether the involved application is managed or not.

  • With a managed client and managed server that are running in managed scope, the client will load the right assembly based on the version policy on the machine. This is true if versioning is used in the assembly.

  • When you are using a fixed GUID in the managed client, and you create a class using the version policy to get to an old version of the assembly, the fixed GUID in the code will be used during activation to extract the service information from the catalog. The service information returned will be the latest registered assembly using this GUID. This service information will be used to create the object, which may be a newer version than the client requests. The client will get a typecast exception when attempting to cast from the actually created object (version 2) to the referenced object in the client (version 1).

  • Consider a situation with a managed client, managed server, and no fixed GUIDs, and you change only the build number. If you are using .NET Framework 1.0, the following will happen: New GUIDs will be generated, but the type library will still have the same version number since type libraries only have two numbers for the version. The client may still be able to work, but if version 2 is installed over version 1, version 1 is then uninstalled, and the type library for version 1 will be unregistered.

    The solution to this is to use .NET Framework version 1.1. In this version, the type library is versioned independently of the assembly. This implies that when changing the assembly version number, the type library version should be changed. If you cannot install version 1.1 of the .NET Framework, you can solve this problem by using only major and minor version numbers.

  • Here's another scenario: unmanaged client with a managed server, no fixed GUIDs used. The client will use a GUID to create the component. Interoperability will resolve the GUID to a name, and then version policy gets applied. If version 1 and version 2 of an assembly are on a machine, and the policy is used to get to version 2, the unmanaged client will get version 2.

    Install version 1, install version 2, then uninstall version 1. Now the client cannot create the component unless there is version policy to redirect to version 2. In addition, registry entries must exist for version 1 registration information. One way to create registry information for an uninstalled version 1 is to use the COM+ aliasing feature on Window XP.

Serviced Components

A serviced component is a component that uses the functionality from Enterprise Services. A serviced component uses, for instance, transaction support, JIT, or object pooling that Enterprise Services handles for you. To be able to access these features, you need to derive your components from the System.EnterpriseServices.ServicedComponent class. To configure your serviced component, you can apply custom attributes to your class. There are three important factors of attributes to remember: the scope, the unconfigured default value, and the configured default value.

The scope of the attributes added to the class can apply to methods, classes, or the whole assembly. It depends on where you put the attribute.

The configured default value is the value the attribute will be given if you omit to specify a value for the attribute. An example of this is the attribute JustInTimeActivation, which has a configured default value of true—for example, [JustInTimeActivation=True]. If the JustInTimeActivation attribute is omitted completely from the code, its default value will be false. This is an unconfigured default value.

There exists many attributes that can be used to configure the COM+ application, but in the following sections we outline the more or less mandatory attributes that we have found quite useful for scalability issues. We also show a simple example of how each of the attributes are used.

ApplicationActivation

This attribute tells the configuration of the COM+ application if the COM+ application should run in the same process as the client (library) or in its own process (server). The attribute does not have any unconfigured default value, and the configured default value is true, which means that the assembly will run as a library in COM+. This is the optimal solution when you want performance, since running the assembly in its own process forces the application to marshal over boundaries that are quite expensive seen from a performance point of view.

Using System;
Using System.Reflection;
Using System.EnterpriseServices;
[ApplicationName("MyTimeReportComponents")]
[ApplicationActivation(ActivationOption.Library)]
public class TimeReport:System.EnterpriseServices.ServicedComponent
{
      public string GetSubmittedReports(string Userid)
      {
      //Code here
      }
public string SaveReport(string Userid,string TimeReport)
      {
      //Code here
      }
}

ApplicationQueueing

To be able to read from message queues, the serviced component needs to define this attribute. The scope is for the assembly, and it has no unconfigured or configured values.

[assembly: ApplicationQueuing(Enabled=true, QueueListenerEnabled=true)]
public class TimeReport:System.EnterpriseServices.ServicedComponent
{
...
}

AutoComplete

When intercepting in transactions, the attribute AutoComplete can be used on methods to tell the serviced component to automatically commit the transaction if no error occurred in the method. If an error occurs, the transaction will be aborted. The unconfigured default value for this attribute is false, and the configured one is true. For example, if the attribute is not specified, the AutoComplete is false, and if the attribute is specified, the default is true.

[AutoComplete]
public string SaveReport(string Userid,string TimeReport)
{
       //Code here
}

ConstructionEnabled

This attribute makes it possible to specify a constructor string that is editable from the COM+ application catalog via the Component Services Manager Console. By adding this attribute to the class, you do not need to hard code the database connection in the data class, for instance; instead you can specify it in the catalog.


[assembly: ConstructionEnabled(Default="myDefaultValue")]
public class TimeReport:System.EnterpriseServices.ServicedComponent
{
      ...
}

Transactions

As mentioned before, this attribute gives you the opportunity to specify what kind of transaction the class needs. The unconfigured default value for this attribute is false, and the configured default value is TransactionOption.Required, TransactionIsolationlevel.Serializable, and Timeout. If you specify TransactionOption.Required, a new transaction will be started; if not, the caller already has a transaction you can participate in.

[Transaction(TransactionOption.Required)]
public class TimeReport:System.EnterpriseServices.ServicedComponent
{
      //...
}

JustInTimeActivation

This attribute applies to the class and allows you to create an object as nonactive and context only. What you get is only a "faked" object, and the real object is created the first time you try to access a method or property on the object. When the method returns, the object is deactivated. When the object is deactivated, it will be returned to the pool if the ObjectPoolingAttribute is used; if it is not, the object is destroyed. All resources that the object was using are released.

Note 

When you use the JustInTimeActivationAttribute, do not turn off the JIT compiling by using the Component Services Admin tool.

[JustInTimeActivation]
public class TimeReport:System.EnterpriseServices.ServicedComponent
{
      ...
}

ObjectPooling

This attribute makes it possible to define for a class how many objects should be pooled (cached) for later use. If this attribute is used, the objects are not created from scratch when a client requests a specific object. COM+ looks into the object pool to find an existing object and returns a reference to that object to the caller. The object is returned to the pool when the caller has finished working with the object. This is an important attribute that is used, for instance, to limit the number of concurrent connections to a database by specifying how many objects of the data class may exist in the pool. The creationTimeout parameter is the time (here 25 seconds) the process will wait for a free object from the pool before the creation will timeout and generate an exception.

[ObjectPooling(Enabled=true,MinPoolSize=1,MaxPoolSize=8,CreationTimeOut=25000)]
public class TimeReport:System.EnterpriseServices.ServicedComponent
{
...
}

Team LiB
Previous Section Next Section