COM+ Queued Components

.NET has a built-in mechanism for invoking a method call on an object: using a delegate asynchronously. The client creates a delegate class that wraps the method it wants to invoke synchronously, and the compiler provides definition and implementation for a BeginInvoke( ) method, which asynchronously calls the required method on the object. The compiler also generates the EndInvoke( ) method to allow the client to poll for the method completion. Additionally, .NET provides a helper class called AsyncCallback to manage asynchronous callbacks from the object once the call is done.

Compared with COM+ queued components, the .NET approach leaves much to be desired. First, .NET does not support disconnected work. Both the client and the server have to be running at the same time, and their machines must be connected to each other on the network. Second, the client’s code in the asynchronous case is very different from the usual synchronous invocation of the same method on the object’s interface. Third, there is no built-in support for transactional forwarding of calls to the server, nor is there an auto-retry mechanism. In short, you should use COM+ queued components if you want to invoke asynchronous method calls in .NET.

The ApplicationQueuing assembly attribute is used to configure queuing support for the hosting COM+ application. The ApplicationQueuing attribute has two public properties that you can set. The Boolean Enabled property corresponds to the Queued checkbox on the application’s queuing tab. When set to true, it instructs COM+ to create a public message queue, named as the application, for the use of any queued components in the assembly. The second public property of ApplicationQueuing is the Boolean QueueListenerEnabled property. It corresponds to the Listen checkbox on the application’s queuing tab. When set to true, it instructs COM+ to activate a listener for the application when the application is launched. For example, here is how you enable queued component support for your application and enable a listener:

//Must be a server application to use queued components
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationQueuing(Enabled = true,QueueListenerEnabled = true)]

The ApplicationQueuing attribute has an overloaded default constructor that sets the Enabled attribute to true and the QueueListenerEnabled attribute to false. As a result, the following two statements are equivalent:

[assembly: ApplicationQueuing]
[assembly: ApplicationQueuing(Enabled = true,QueueListenerEnabled = false)]

Configuring Queued Interfaces

In addition to enabling queued component support at the application level, you must mark your interfaces as capable of receiving queued calls. You do that by using the InterfaceQueuing attribute. InterfaceQueuing has one public Boolean property called Enabled that corresponds to the Queued checkbox on the interface’s Queuing tab.

[InterfaceQueuing(Enabled = true)]
public interface IMyInterface
{
   void MyMethod(  );
}

The InterfaceQueuing attribute has an overloaded default constructor that sets the Enabled property to true and a constructor that accepts a Boolean parameter. As a result, the following three statements are equivalent:

[InterfaceQueuing] 
[InterfaceQueuing(true)]
[InterfaceQueuing(Enabled = true)]

Note that your interface must adhere to the queued components design guidelines discussed in Chapter 8, such as no out or ref parameters. If you configure your interface as a queued interface using the InterfaceQueuing attribute and the interface is incompatible with queuing requirements, the registration process fails.

A Queued Component’s Managed Client

The client of a queued component cannot create the queued component directly. It must create a recorder for its calls using the queue moniker. A C++ or a Visual Basic 6.0 program uses the CoGetObject( ) or GetObject( ) calls. A .NET managed client can use the static method BindToMoniker( ) of the Marshal class, defined as:

public static object BindToMoniker(string monikerName);

BindToMoniker( ) accepts a moniker string as a parameter and returns the corresponding object. The Marshal class is defined in the System.Runtime.InteropServices namespace.

The BindToMoniker( ) method of the Marshal class makes writing managed clients for a queued component as easy as if it were a COM client:

using System.Runtime.InteropServices;//for the Marshal class

IMyInterface obj;
obj =(IMyInterface)Marshal.BindToMoniker("queue:/new:MyNamespace.MyComponent");
obj.MyMethod(  );//call is recorded

In the case of a COM client, the recorder records the calls the client makes. The recorder only dispatches them to the queued component queue (more precisely, to its application’s queue) when the client releases the recorder. A managed client does not use reference counting, and the recorded calls are dispatched to the queued component queue when the managed wrapper around the recorder is garbage collected. The client can expedite dispatching the calls by explicitly forcing the managed wrapper around the recorder to release it, using the static DisposeObject( ) method of the ServicedComponent class, passing in the recorder object:

using System.Runtime.InteropServices;//for the Marshal class

IMyInterface obj;
obj =(IMyInterface)Marshal.BindToMoniker("queue:/new:MyNamespace.MyComponent");
obj.MyMethod(  );//call is recorded

//Expedite dispatching the recorded calls by disposing of the recorder
ServicedComponent sc = obj as ServicedComponent;
If(sc !=null)
  ServicedComponent.DisposeObject(sc);

You can use the IDisposable interface instead of calling DisposeObject().

Queued Component Error Handling

Due to the nature of an asynchronous queued call, managing a failure on both the client’s side (failing to dispatch the calls) and the server’s side (repeatedly failing to execute the call—a poison message) requires a special design approach. As discussed in Chapter 8, both the clients and server can use a queued component exception class to handle the error. You can also provide your product administrator with an administration utility for moving messages between the retry queues.

Queued component exception class

You can designate a managed class as the exception class for your queued component using the ExceptionClass attribute. Example 10-15 demonstrates using the ExceptionClass attribute.

Example 10-15. Using the ExceptionClass attribute to designate an error-handling class for your queued component

using COMSVCSLib;

public class MyQCException : IPlaybackControl,IMyInterface
{   
   public void FinalClientRetry(  ) {...}   
   public void FinalServerRetry(  ) {...}   
   public void MyMethod(  ){...}
}
[ExceptionClass("MyQCException")]
public class MyComponent :ServicedComponent,IMyInterface
{...}

In Example 10-15, when you register the assembly containing MyComponent with COM+, on the component’s Advanced tab, the Queuing exception class field will contain the name of its exception class—in this case, MyQCException, as shown in Figure 10-3.

After registering the component in Example 10-15 with COM+, its Advanced tab contains the exception class

Figure 10-3.  After registering the component in Example 10-15 with COM+, its Advanced tab contains the exception class

You need to know a few more things about designating a managed class as a queued component’s exception class. First, it has nothing to do with .NET error handling via exceptions. The word exception is overloaded. As far as .NET is concerned, a queued component’s exception class is not a .NET exception class. Second, the queued component exception class has to adhere to the requirements of a queued component exception class described in Chapter 8. These requirements include implementing the same set of queued interfaces as the queued component itself and implementing the IPlaybackControl interface. To add IPlaybackControl to your class definition you need to add a reference in your project to the COM+ Services type library. IPlaybackControl is defined in the COMSVCSLib namespace.

The MessageMover class

As explained in Chapter 8, COM+ provides you with the IMessageMover interface, and a standard implementation of it, for moving all the messages from one retry queue to another. Managed clients can access this implementation by importing the COM+ Services type library and using the MessageMover class, defined in the COMSVCSLib namespace. Example 10-16 implements the same use-case as Example 8-2.

Example 10-16. MessageMover is used to move messages from the last retry queue to the application’s queue

using COMSVCSLib;

IMessageMover messageMover;
int moved;//How many messages were moved

messageMover = (IMessageMover) new MessageMover(  );

//Move all the messages from the last retry queue to the application's queue 
messageMover.SourcePath = @".PRIVATE$MyApp_4";
messageMover.DestPath   = @".PUBLIC$MyApp"; 

moved = messageMover.MoveMessages(  );
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset