AddThis Social Bookmark Button

Print

.NET Serviced Components
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

COM+ Context Attributes

You can decorate (apply attributes to) your class with two context-related attributes. The attribute MustRunInClientContext informs COM+ that the class must be activated in its creator's context:

[MustRunInClientContext(true)]
public class MyComponent :ServicedComponent
{...}

When you register the class above with COM+, the "Must be activated in caller's context" checkbox on the component's Activation tab is selected in the Component Services Explorer. If you do not use this attribute, the registration process uses the default COM+ setting when registering the component with COM+ --not enforcing same-context activation. As a result, using MustRunInClientContext with a false parameter passed to the constructor is the same as using the COM+ default:

[MustRunInClientContext(false)]

Using attributes with the COM+ default values (such as constructing the MustRunInClientContext attribute with false) is useful when you combine it with the /reconfig switch of RegSvcs. For example, you can undo any unknown changes made to your component configuration using the Component Services Explorer and restore the component configuration to a known state.

The MustRunInClientContext attribute class has an overloaded default constructor. If you use MustRunInClientContext with no parameters, the default constructor uses true for the attribute value. As a result, the following two statements are equivalent:

[MustRunInClientContext]
[MustRunInClientContext(true)]

The second COM+ context-related attribute is the EventTrackingEnabled attribute. It informs COM+ that the component supports events and statistics collection during its execution:

[EventTrackingEnabled(true)]
public class MyComponent2:ServicedComponent
{...}

The statistics are displayed in the Component Services Explorer. When you register this class with COM+, the "Component supports events and statistics" checkbox on the component's Activation tab is checked in the Component Services Explorer. If you do not use this attribute, the registration process does not use the default COM+ setting of supporting events when registering the component with COM+. The .NET designers made this decision consciously to minimize creation of new COM+ contexts for new .NET components; a component that supports statistics is usually placed in it own context.

The EventTrackingEnabled attribute class also has an overloaded default constructor. If you construct it with no parameters, the default constructor uses true for the attribute value. As a result, the following two statements are equivalent:

[EventTrackingEnabled]
[EventTrackingEnabled(true)]

COM+ Object Pooling

The ObjectPooling attribute is used to configure every aspect of your component's object pooling. The ObjectPooling attribute enables or disables object pooling and sets the minimum or maximum pool size and object creation timeout. For example, to enable object pooling of your component's objects with a minimum pool size of 3, a maximum pool size of 10, and a creation timeout of 20 milliseconds, you would write:

[ObjectPooling(MinPoolSize = 3,MaxPoolSize = 10,CreationTimeout = 20)]
public class MyComponent :ServicedComponent
{...}

Related Reading

COM and .NET Component ServicesCOM and .NET Component Services
By Juval Lwy
Table of Contents
Index
Sample Chapter
Full Description

The MinPoolSize, MaxPoolSize, and CreationTimeout properties are public properties of the ObjectPooling attribute class. If you do not specify values for these properties (all or just a subset) when your component is registered, the default COM+ values are used for these properties (a minimum pool size of 0, a maximum pool size of 1,048,576, and a creation timeout of 60 seconds).

The ObjectPooling attribute has a Boolean property called the Enabled property. If you do not specify a value for it (true or false), the attribute's constructor sets it to true. In fact, the attribute's constructor has a few overloaded versions--a default constructor that sets the Enabled property to true and a constructor that accepts a Boolean parameter. All constructors set the pool parameters to the default COM+ value. As a result, the following three statements are equivalent:

[ObjectPooling]
[ObjectPooling(true)]  
[ObjectPooling(Enabled = true)]

TIP:   If your pooled component is hosted in a library application, then each hosting Application Domain will have its own pool. As a result, you may have multiple pools in a single physical process, if that process hosts multiple Application Domains.

Under COM, the pooled object returns to the pool when the client releases its reference to it. Managed objects do not have reference counting--.NET uses garbage collection instead. A managed pooled object returns to the pool only when it is garbage collected. The problem with this behavior is that a substantial delay between the time the object is no longer needed by its client and the time the object returns to the pool can occur. This delay may have serious adverse effects on your application scalability and throughput. An object is pooled because it was expensive to create. If the object spends a substantial portion of its time waiting for the garbage collector, your application benefits little from object pooling.

There are two ways to address this problem. The first solution uses COM+ JITA (discussed next). When you use JITA, the pooled object returns to the pool after every method call from the client. The second solution requires client participation.

ServicedComponent has a public static method called DisposeObject( ), defined as:

public static void DisposeObject(ServicedComponent sc);

When the client calls DisposeObject( ), passing in an instance of a pooled serviced component, the object returns to the pool immediately. DisposeObject( ) has the effect of notifying COM+ that the object has been released. Besides returning the object to the pool, DisposeObject( ) disposes of the context object hosting the pooled object and of the proxy the client used.

For example, if the component definition is:

public interface IMyInterface
{    
   void MyMethod(  );
}
  
[ObjectPooling]
public class MyComponent : ServicedComponent,IMyInterface
{
   public void MyMethod(  ){}
} 

When the client is done using the object, to expedite returning the object to the pool, the client should call DisposeObject( ):

IMyInterface obj;
Obj = (IMyInterface) new MyComponent(  );
obj.MyMethod(  );
ServicedComponent sc = obj as ServicedComponent;
If(sc != null)
	ServicedComponent.DisposeObject(sc);

However, calling DisposeObject( ) directly is ugly. First, the client has to know that it is dealing with an object derived from ServicedComponent, which couples the client to the type used and renders many benefits of interface-based programming useless. Even worse, the client only has to call DisposeObject( ) if this object is pooled, which couples the client to the serviced component's configuration. What if you use object pooling in only one customer site, but not in others? This situation is a serious breach of encapsulation--the core principle of object-oriented programming.

The solution is to have ServicedComponent implement a special interface (defined in the System namespace) called IDisposable, defined as:

public interface IDisposable
{
    void Dispose(  );
}

ServicedComponent implementation of Dispose( ) returns the pooled object to the pool.

Having the Dispose( ) method on a separate interface allows the client to query for the presence of IDisposable and always call it, regardless of the object's actual type:

IMyInterface obj;
obj = (IMyInterface) new MyComponent(  );
obj.MyMethod(  );
 
//Client wants to expedite whatever needs expediting:
IDisposable disposable = obj as IDisposable;
if(disposable != null)     
   disposable.Dispose(  );

The IDisposable technique is useful not only with serviced components, but also in numerous other places in .NET. Whenever your component requires deterministic disposal of the resources and memory it holds, IDisposable provides a type-safe, component-oriented way of having the client dispose of the object without being too coupled to its type.

COM+ Just-in-Time Activation

.NET managed components can use COM+ JITA to efficiently handle rich clients (such as .NET Windows Forms clients), as discussed in Chapter 3.

To enable JITA support for your component, use the JustInTimeActivation attribute:

[JustInTimeActivation(true)]
public class MyComponent :ServicedComponent
{..}

When you register this component with COM+, the JITA checkbox in the Activation tab on the Component Services Explorer is selected. If you do not use the JustInTimeActivation attribute, JITA support is disabled when you register your component with COM+ (unlike the COM+ default of enabling JITA). The JustInTimeActivation class default constructor enables JITA support, so the following two statements are equivalent:

[JustInTimeActivation]
[JustInTimeActivation (true)]

Enabling JITA support is just one thing you need to do to use JITA. You still have to let COM+ know when to deactivate your object. You can deactivate the object by setting the done bit in the context object, using the DeactivateOnReturn property of the ContextUtil class. As discussed at length in Chapter 3, a JITA object should retrieve its state at the beginning of every method call and save it at the end. Example 10-3 shows a serviced component using JITA.

Example 10-3: A serviced component using JITA

public interface IMyInterface
{    
   void MyMethod(long objectIdentifier);
}
  
[JustInTimeActivation(true)]
public class MyComponent :ServicedComponent,IMyInterface
{ 
   public void MyMethod(long objectIdentifier)   
   {
      GetState(objectIdentifier);     
      DoWork(  );     
      SaveState(objectIdentifier);
	  //inform COM+ to deactivate the object upon method return
      ContextUtil.DeactivateOnReturn = true;
   }
   //other methods
   protected void GetState(long objectIdentifier){...}
   protected void DoWork(  ){...}
   protected void SaveState(long objectIdentifier){...}
} 

You can also use the Component Services Explorer to configure the method to use auto-deactivation. In that case, the object is deactivated automatically upon method return, unless you set the value of the DeactivateOnReturn property to false.

Using IObjectControl

If your serviced component uses object pooling or JITA (or both), it may also need to know when it is placed in a COM+ context to do context-specific initialization and cleanup. Like a COM+ configured component, the serviced component can use IObjectControl for that purpose. The .NET base class ServicedComponent already implements IObjectControl, and its implementation is virtual--so you can override the implementation in your serviced component, as shown in Example 10-4.

Example 10-4: A serviced component overriding the ServicedComponent implementation of IObjectControl

public class MyComponent :ServicedComponent
{ 
   public override void Activate(  ) 
   {     
      //Do context specific initialization here
   }   
   public override void Deactivate(  ) 
   {     
      //Do context specific cleanup here
   }   
   public override bool CanBePooled(  )   
   {
	    return true;   
   }
   //other methods
}

If you encounter an error during Activate( ) and throw an exception, then the object's activation fails and the client is given an opportunity to catch the exception.

IObjectControl, JITA, and Deterministic Finalization

To maintain JITA semantics, when the object deactivates itself, .NET calls DisposeObject( ) on it explicitly, thus destroying it. Your object can do specific cleanup in the Finalize( ) method (the destructor in C#), and Finalize( ) will be called as soon as the object deactivates itself, without waiting for garbage collection. If the object is a pooled object (as well as a JITA object), then it is returned to the pool after deactivation, without waiting for the garbage collection.

You can also override the ServicedComponent implementation of IObjectControl.Deactivate( ) and perform your cleanup there.

In any case, you end up with a deterministic way to dispose of critical resources without explicit client participations. This situation makes sharing your object among clients much easier because now the clients do not have to coordinate who is responsible for calling Dispose( ).

TIP:  COM+ JITA gives managed components deterministic finalization, a service that nothing else in .NET can provide out of the box.

Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11

Next Pagearrow