Vision

Creating enterprise services queued components in C#

March 3, 2006

Introduction

Some time ago, about 5 years or so, the major part of our development activities consisted of ASP and VB6. In that period we used to use MTS, and later COM+, all the time. We remember that around the time we switched to developing .NET applications most developers were very pessimistic about the future of COM+ (or Enterprise Services, as it is called today). We also noticed the technology wasn't used much anymore in the companies we've worked for, although these companies were the larger companies in the Netherlands. If we look at ourselves, in the last couple of years we haven’t used Enterprise Services at all. We didn’t really feel the need to. Recently we’ve totally rediscovered Enterprise Services and we can hardly wait for the moment Windows Communication Foundation is released officially, a product which will take developing enterprise applications to the next level. At the moment we're building some queued components in Enterprise Services and we’re quite enthousiastic about that. In this article we describe how to create queued components, maybe you'll get enthousiastic too.

Why use queued components?

When doing asynchronous communication queued components are very handy. All calls to a queued component are actually stored in a message queue (in MSMQ). As soon as the queued component is ready to do some processing the message is picked up and processed by the queued component.

If the processing of this message fails the message is placed in another queue, a process which repeats itself until the message is processed successfully or until the message is placed in a dead message queue. After the first attempt it takes 1 minute before the same message is processed again. The second retry takes a wait time of 2 minutes. The wait time increases until the 5th and final retry attempt, which takes place 16 minutes after the previous (4th) attempt.

Additionally, Enterprise Services offers various services to facilitate enterprise level component development. For example, Enterprise Services makes it easy to specify the user identity under which the queued component will run. As another example, you can specify and change the maximum number of concurrent instances of a queued component. This ensures your server will not become too busy. As a final example, via Enterprise Services you can monitor how many instances of your queued component are running and how well these instances are doing. Summarizing, queued components offer an easy way to do asynchronous communication on the enterprise level in a robust way.

How to create a queued component?

The first step to complete to create a queued component is to create a new project using the class library template. You should add a reference to the System.EnterpriseServices assembly. You will always have to specify one or more interfaces for the queued component and add metadata to those interface or interfaces. Clients can only communicate with queued components through these interfaces. For this example we’ve created two test interfaces, IMyFirstInterface and IMySecondInterface. Here’s the definition of the first interface, the second one looks identical apart from the names which are slightly different, so we won't list that one here.

using System;
using System.EnterpriseServices;

namespace MyQueuedComponent
{
 [InterfaceQueuing(Enabled = true)]
 public interface IMyFirstInterface
 {
   void Do(string strArg);
 }
}

Now you're ready to create a class which implements the IMyFirstInterface and IMySecondInterface interfaces. This is the class which is executed when clients call the queued component. This class must inherit from the ServicedComponent class which is located in the System.EnterpriseServices namespace. Also, you should add an InterfaceQueuing attribute for every interface implemented in this class. The definition for our example looks like this:

using System;
using System.EnterpriseServices;

namespace MyQueuedComponent
{
 [InterfaceQueuing(Interface = "IMyFirstInterface")]
 [InterfaceQueuing(Interface = "IMySecondInterface")]
 public class MyTest : ServicedComponent, IMyFirstInterface, IMySecondInterface
 {
  public MyTest()
  {
  }

  #region IMyFirstInterface Members

  public void Do(string strArg)
  {
    // Do something...
  }

  #endregion

  #region IMySecondInterface Members

  ...
  #endregion

 }
}

You'll also need to modify the AssemblyInfo.cs file. Make sure to import the following namespace:

using System.EnterpriseServices;

Queued components have to be strong named, so you should create a strong named key file and add a reference to it in AssemblyInfo.cs:

[assembly: AssemblyKeyFile(@"[path]\PublicPrivate.snk")]

If you need more information about strong naming assemblies check out our following article Establishing the public key token: the difference between -t and -T.

Now you need to add Enterprise Service specific metadata to the AssemblyInfo.cs file. You'll need to define an application name via the ApplicationName attribute. This name will be used as the name for the COM+ application. The COM+ application will be installed automatically once you register the queued component . This name will also serve as a basis for the names of several message queues which are also created automatically upon installation of the queued component.

You should specify the application activation mode via the ApplicationActivation attribute. This needs to be set to 'Server'.

Finally, you have to define a couple of application queueing options which can be set via the ApplicationQueuing attribute. The most interesting option here is the maximum number of concurrent instances of your queued component. Instances of the queued component are created as needed (if there are no messages left in the queue there will only be one thread running, the queue listener). The valid range for this value is 0 to 1000. By default, this number is 16 multiplied by the number of CPUs in the server.

To complete the modification of AssemblyInfo.cs file add the following Enterprise Service specific metadata:

[assembly: ApplicationName("MyQueuedComponent")]
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationQueuing(Enabled=true, QueueListenerEnabled=true, MaxListenerThreads=10)]

You also might want to set the ApplicationAccessControl attribute. This attribute can be used to configure all settings on the hosting COM+ application security tab. If you omit this attribute you will get the following warning upon assembly registration: 'The assembly does not declare an ApplicationAccessControl attribute. Application security will be enabled by default.'. If you want to prevent this message add the following attribute to AssemblyInfo.cs.

[assembly: ApplicationAccessControl(true)]

Installation

Now that we've created a basic queued component we can register it as an enterprise service. To do this open a Visual Studio command prompt (Start/Programs/Microsoft Visual Studio.NET 2003/Visual Studio .NET tools/Visual Studio .NET 2003 Command Prompt). Then navigate to the directory that contains the MyQueuedComponent assembly. Type 'regsvcs MyQueuedComponent.dll' at the command line to register the assembly. At this point MSMQ queues are created (called MyQueuedComponent, MyQueuedComponent_0 to MyQueuedComponent_4 and MyQueuedComponent_deadqueue), as well as a COM+ application (called MyQueuedComponent).

Configuration

Next, it's time to configure the MyQueuedComponent COM+ application. Go to Control panel/Administrative tools/Component Services. Open the Component Services node, then open the My Computer node and finally open the COM+ applications folder. The MyQueuedComponent COM+ application should be listed.
- Right-click properties, go to the 'Queueing' tab and check that the queued checkbox is checked and also that the Listen checkbox is checked.
- Set MSMQ Message Authentication to 'Do not authenticate messages'. Message authentication ensures that the message was not modified since the moment it was sent by the client. It also verifies the source of a message. This is done by attaching a digital signature to the message. The MSMQ Queue manager is responsible for performing the verification process.
- Go to the 'Activation' tab and check that the 'Server application' radio button is checked.
- Check the 'Run application as NT service' button. Click the 'Setup new service' button and click 'Create'. A Windows service for the MyQueuedComponent queued component will be created automatically.
- Go to the 'Identity' tab. Choose 'Local system' as System account. You could also choose a custom account or the Local Service account.

Choosing the correct identity

Under Windows XP/Windows 2000 and Windows server 2003 the Local System account scope is no longer restricted to the local machine. Via this account it's possible to access network resources. It will access those resources via the computer's domain identity (MyDomain\MyComputerName$). The Local System account has no network security credentials but provides very powerful local privileges. Services running under the Local System account have full access to the local machine (as this account is a hidden member of the local Administrators group).

Probably your application doesn't need this much privileges. Instead you could choose the Local Service acocunt which has limited access to local resources and runs under the anonymous user account. This account can be used to access resources that allow access to the anonymous user account or the Everyone group. In theory this account can have network access if network resources allow access to the Guest account with no password. In practice, network administrators will never allow this. The moral is: don't use the Local Service account for accessing the network.

Checking if everything works as expected

First check if all the queues are created correctly. Go to Computer Management, Message queueing. and check the private queues node. MyQueuedComponent, MyQueuedComponent_0 to MyQueuedComponent_4 and MyQueuedComponent_deadqueue should be listed. Open the Services MMC snap-in (or type 'services.msc' from the command prompt) and ensure the MyQueuedComponent service is listed. If you start the service the queued component is ready for processing requests, but before you do that you should create a test client and call the queued component. If the calls are successfull you should see new messages in the MyQueuedComponent queue. When you start the MyQueuedComponent service you'll see the messages disappear. The following code shows how to call a queued component.

First import the following namespace:

using System.Runtime.InteropServices;

The code in the caller should look something like this:

string strUri = "queue:/new:MyQueuedComponent.MyTest";
IMyFirstInterface objQueuedComponent = (IMyFirstInterface) Marshal.BindToMoniker(strUri);

// dsData is an example dataset containing some data.
// The initialization of this dataset isn't shown in this example.
string strData = dsData.GetXml();
objQueuedComponent.Do(strData);
Marshal.ReleaseComObject(objQueuedComponent);

Debugging

It's not easy to debug a queued component. You can attach to the process in which a queued component is executed but in that case you have to remember that all method calls are processed asynchronously, so you don't have exact control over when a method is called. The best way to debug a queued component is to create an adapter class which performs the actual work for the queued component. Our MyTest class will do nothing more then call this adapter. Since the adapter performs the actual task you can also create unit tests directly calling the adapter class. Such an adapter could look like this:

using System;
using System.Diagnostics;

namespace MyQueuedComponent
{
public class MyTestAdapter : IMyFirstInterface, IMySecondInterface
{
 public MyTestAdapter()
 {
 }
 #region IMyFirstInterface Members

 public void Do(string strArg)
 {
  Trace.WriteLine(strArg);
 }
 #endregion

 ....
}

The Do() method of the IMyFirstInterface interface of the MyTest class will look like this:

 public void Do(string strArg)
 {
   IMyFirstInterface objAdapter = new TestProcessorAdapter();
   objAdapter.Do(strArg);
 }

A test harness can reference the MyQueuedComponent assembly and call the adapter directly, exactly like the MyTest class does. This approach makes debugging queued components significantly easier.

IPersist interface

As you may have noticed, our test Do() method receives a string as an argument. This was not a coincidence. The queued component is hosted in a different process as the client, so data needs to be marshalled between processes. But with queued components it's not enough if the type you're trying to pass as an argument is serializable. Parameter types must implement the IPersistStream which effectively makes strings the easiest way of sending arguments to queued component methods. Since lot's of .NET applications use DataSets to pass around data we'll show a way to convert a dataset to a string and back again. You can convert a DataSet to a string in the following manner:

string strResources = dsResources.GetXml();

And if you want to go back you can create your own Converter utility class and add the following method:

///
/// Converts string containing XML data to DataSet.
///

public static DataSet ToDataSet(string strXmlData)
{
 StringReader objReader = new StringReader(strXmlData);
 DataSet dsData = new DataSet();
 dsData.ReadXml(objReader);

 return dsData;
}

Troubleshooting

If the queued component doesn't work we've found that reinstalling the queued component works wonders. If you want to uninstall the queued component make sure the MyQueuedComponent windows service is stopped (if present). Type regsvcs -u MyQueuedComponent.dll at the command line to uninstall the assembly. If not deleted automatically, delete the MyQueuedComponent COM+ application in the Component Services MMC snap-in manually.

Monitoring

If you want to monitor how your queued component is doing go to Control panel\Administrative tools\Component services. Open console root\Component services\My computer\Distributed transaction coordinator. The Transaction list and Transaction statistics nodes will show the current activities of your queued component.

Config files

A queued component is a library (.dll). Once installed and configured as a Windows service the queued component will be loaded into a host. This makes it harder to specify an application configuration file, as the app.config of the host is used. A queued component is hosted by dllhost.exe (which, by default, can be found on the following location: C:\WINDOWS\System32\dllhost.exe). If you want to specify application settings you could modify the app.config file of dllhost.exe, which is kinda impractical as this information would be shared by all applications which are hosted by dllhost.exe.

Instead of using this mechanism, we like to use the Enterprise Service construction string. Go to Control panel/Administrative tools/Component Services. Open the Component Services node, then open the My Computer node and open the COM+ applications folder. The MyQueuedComponent COM+ application should be listed. Open this node as well and finally open MyQueuedComponent.MyQueuedComponent. Right-click the queued component and click on the Activation tab. Check the 'Enable object construction' checkbox. Now you can specify a value for the Constructor String. The constructor string is passed to the queued component once it's initialized. As it's quite impractical to store an entire XML application configuration in the constructor string we typically use this to specify the location of an XML file which contains the application configuration information.

If you want to access the constructor string just add an override of the Construct() method to the MyQueuedComponent.MyQueuedComponent class, like this:

protected override void Construct(string strConnectionString)
{
 ConfigHelper.ConstructionString = strConnectionString;
 base.Construct (strConnectionString);
}

Conclusion

This article contains handy information if you want to develop Enterprise Service queued components in .NET. We hope it was of use to you! We'd love any feedback, so if you want mail us at: info@lcbridge.nl.

« back to overview page