Using COM+ Services in .NET

 

Tim McCarthy, InterKnowlogy
Paul D. Sheriff, PDSA, Inc.

February 2002

Summary: Add new Microsoft .NET components to existing COM and COM+ applications and they will be able to work together; this will help you if you need to develop a .NET application that can do things like participate in transactions, take advantage of role-based security, or interact with a queue. (14 printed pages)

Objectives

  • Learn about using COM+ Services in Microsoft® .NET.
  • Create a serviced component.
  • Deploy a serviced component.

Assumptions

The following should be true for you to get the most out of this document:

  • You have used Microsoft Transaction Server (MTS) and distributed transactions in Microsoft Visual Basic® 6.0.
  • You have used role-based security with COM+ Services.
  • You have created and used queues in COM+ Services.
  • You are very familiar with .NET classes.
  • You are familiar with creating console applications in .NET.

Contents

Using COM+ Services in .NET
Developing Transaction-Based Components
Role-Based Security
Using Queued Components
What's Different in COM+ Since Visual Basic 6.0?
Summary

Using COM+ Services in .NET

You probably have used COM+ Applications to host components that you have written in Visual Basic or C++. COM+ offers many valuable services, such as transactions, queued components, just-in-time activation, role-based security, shared properties, etc. One of the main attractions of using COM+ to host components was that you could change the way the component behaved without having to write any code, such as marking a component's transaction support as Required. By setting a radio button on your COM+ component from within the Component Services MMC snap-in, every time your component is created, it is created in the context of a COM+ transaction. When a component uses COM+ transactions, any database transactions are handled by the Distributed Transaction Coordinator (DTC). Figure 1 shows an example of setting the Required Transaction option from within the Component Services interface.

Figure 1. Sample COM+ component that requires a transaction

Setting the security for your component is just as easy as setting the transaction support. You can choose which users can run which components, and even which methods, without recompiling your code. You choose by using the COM+ Services snap-in.

.NET Can Use All COM+ Services

In the .NET Framework, you can still use all of the services that COM+ offers, as long as your classes derive from the System.EnterpriseServices.ServicedComponent class. Any class that derives from the ServicedComponent class will be hosted by COM+ services and can use any of the available COM+ services. Table 1 shows all of the COM+ services supported in .NET, as well as a short description of each service.

Table 1. COM+ services available

COM+ Service Description
Automatic Transaction Processing Applies declarative transaction-processing features
COM Transaction Integrator (COMTI) Encapsulates CICS and IMS applications in Automation objects
Compensating Resource Managers (CRMs) Applies atomicity and durability properties to non-transactional resources
Just-In-Time Activation Activates an object on a method call and deactivates when the call returns
Loosely Coupled Events Manages object-based events
Object Construction Passes a persistent string value to a class instance on construction of the instance
Object Pooling Provides a pool of ready-made objects
Queued Components Provides asynchronous message queuing
Role-Based Security Applies security permission based on role
Shared Properties Shares state among multiple objects within a server process
Synchronization (Activity) Manages concurrency
XA Interoperability Supports the X/Open transaction-processing model

Reasons to Use COM+ Services in .NET

If you will be writing a .NET application that needs to be able to do things like participate in transactions, take advantage of role-based security, or interact with a queue, you will use the COM+ services offered in .NET. .NET makes these services easy to implement, as you will learn in this document.

**Tip   **If you do not need your .NET code to work with COM+ services, that is, you are only going to be working in the .NET Framework, do not use System.EnterpriseServices, because there is a performance penalty.

Overview of COM+ Component Development

When creating components in .NET that you want to interact with COM+ Services, you perform the following steps. Table 2 includes an explanation of each step.

  1. Create a Class Library.

  2. Create all classes so they inherit from the System.EnterpriseServices.ServicedComponents class.

  3. Create an assembly.

  4. Create a strong name.

    Table 2. Definition of terms used in creating a .NET component

    Term Description
    Class Library A .dll project type that contains classes. There is generally no user interface with this type of project.
    System.EnterpriseServices.ServicedComponents A class in the .NET Framework that is required for you to be able to interact with COM+ Services
    Assembly A description of all of the classes and interfaces within your project
    Strong Name Generates a GUID from your assembly so that your component can be registered with COM+ Services

Developing Transaction-Based Components

In the first part of this document, you will learn to create a .NET component that uses the transactional services of COM+. You will learn how to write both the component and the front-end application that interacts with this component.

Creating a COM+ Transaction Component

There are a few steps that you need to go through to get a .NET component to run under COM+ services. To begin, you have to create a class that derives from the System.EnterpriseServices.ServicedComponent class. This base class gives you all of the appropriate methods and properties needed to interact with COM+ services. You need to mark the class as requiring a new transaction, and mark any methods you create as able to automatically complete the transaction if no errors occur. Let's try it.

  1. Open Microsoft Visual Studio® .NET and create a new project as a ClassLibrary project.

  2. Rename the Class1.vb file to COMPlusServices.vb.

  3. Open the COMPlusServices.vb file and change the class name from Class1 to COMPlusServices.

  4. Type in the code shown below into this new class.

    Imports System.EnterpriseServices
    Imports System.Reflection
    
    '********************************************
    'COM+ Registration details 
    
    'Supply the COM+ application name 
    <Assembly: ApplicationNameAttribute("ComPlusExample")> 
    
    'Supply a strong-name assembly
    <Assembly: _ 
    AssemblyKeyFileAttribute("bin/ComPlusExample.snk")>
    '********************************************
    
    <TransactionAttribute(TransactionOption.Required)> _
     Public Class COMPlusServices
        Inherits ServicedComponent 
    
        Public Sub New()
            MyBase.New()
        End Sub
    
        <AutoComplete()> Public Function DoTransaction() _
         As String
            Return "Success with COM+"
        End Function
    End Class
    

    This code starts out by importing a couple of namespaces to eliminate some typing when declaring components.

  5. Next are COM+ registration details. Enter the following line of code:

    'Supply the COM+ application name 
    <Assembly: ApplicationNameAttribute("ComPlusExample")> 
    

This line assigns the ApplicationNameAttribute a value of ComPlusExample. This is what the name of the COM+ application will be when it is registered in the COM+ Catalog. After the first time this component is called, when you go into the COM+ Applications folder in the MMC snap-in, you will see this as the application name.

The next part of the code declares the AssemblyKeyFileAttribute attribute:

<Assembly: _ 
AssemblyKeyFileAttribute("bin/ComPlusExample.snk")>

This informs the COM+ catalog where the information about the strong name is located. You will create the .SNK file in a later step, but it is the file that describes the component to COM+.

You will now finally declare the Class name, COMPlusServices, using the following code.

<TransactionAttribute(TransactionOption.Required)> _
 Public Public Class COMPlusServices

The attribute in front of this class name informs COM+ that you wish to set the transaction attribute to Required. Adding this line of code is the same as going into the COM+ Applications snap-in, as shown in Figure 1, and setting this attribute manually.

The next line of code in the class inherits from the ServicedComponent within the System.EnterpriseServices namespace.

Inherits ServicedComponent 

If you do not include this line, you will be unable to make this component work under COM+.

Add a Transaction Method

Now that the setup of this class is complete, you can create a method that will actually do something. The function DoTransaction in the code you wrote returns a string value, but shows the syntax you must use to have this method participate in a transaction.

<AutoComplete()> Public Function DoTransaction() As String
    Return "Success with COM+"
End Function

It is important that you prefix this method with the <AutoComplete()> attribute. This means that as long as there is not an exception in this method, SetComplete will be automatically called when this method finishes. If there is an exception in the method, the .NET runtime automatically calls the SetAbort method. This is different than writing COM components in Visual Basic 6.0, where you had to explicitly call SetComplete and SetAbort on your own.

Create a Strong Name

Before compiling your component, you need to give your assembly for this component a strong name. If you do not do this, the COM+ Catalog will not recognize your component and will not be able to register it. Actually, you have already done this via the AssemblyKeyFile attribute that you used earlier, but now you need to create the strong name and associate a GUID with your assembly by using the Strong Name Tool (Sn.exe).

  1. Open a command prompt.

  2. To create the strong name, type the following at a command prompt and then press Enter.

    sn -k ComPlusExample.snk
    
  3. Copy the ComPlusExample.snk file from the root folder of your hard drive (most likely C:/) into the bin directory under the folder where your project is.

You now need to compile this program so it can build the necessary files needed to register this component with COM+. In Visual Studio .NET, on the Build menu, click Build.

Build a Client Test Application

Now that you have built your component, you need to build a client application to call this component and test it out. Create a simple console application where the module file's Main method creates an instance of your new component, and calls the DoTransaction() method. Here are the main steps.

  1. In Visual Basic .NET, create a new console application project.

  2. Add a reference to the component that you just created.

  3. Type in the code shown below.

    Module modMain
        Sub Main()
            Dim objCOMPlus As New _ 
            COMPlusJumpStart.COMPlusServices()
    
            Console.WriteLine(objCOMPlus.DoTransaction)
            Console.ReadLine()
        End Sub
    End Module
    

Try It Out

You are finally ready to run this application and see it work.

  1. Open the Component Services MMC snap-in and verify that your component was dynamically registered into the COM+ Catalog. You should see something that looks like Figure 2.

  2. Compile and run the console application.

    Figure 2. The new .NET serviced component in the COM+ Catalog

Role-Based Security

When you have many users making calls to COM components that run under COM+, you need to verify that only specific people have access to certain components. COM+ allows you to define roles and assign NT users to those roles. Once these roles are in place, you can assign which roles can run which components, and even which methods on those components can be run.

Let's add a method to this same COMPlusServices class to incorporate role-based security. You will create a role called Managers, and in the new method, test to see if the caller is in the Managers role.

Steps to Add Role-Based Security

Instead of directly modifying the COM+ application from the Component Services MMC snap-in to add the security role, you will add a new attribute to your project. You will use the SecurityRoleAttribute class to add the new Managers role. The constructor for this class has two arguments: role (string) and everyone (Boolean). The role argument specifies the name of the role to be created, and the everyone argument specifies whether or not the built-in Everyone group is added to the users of the role.

  1. Add a new security role to the COM+ application by typing in the following code just below the comment COM+ Registration details.

    '********************************************
    'COM+ Registration details 
    
    'Role-based security attribute
    <Assembly: SecurityRoleAttribute("Managers", False)>  
    
  2. Change the security level setting to perform access checks at the process and component level. This enables the COM+ application to have a security call context.

  3. Bring up the COM+ Services snap-in.

  4. Click the Security tab and change the security level, as shown in Figure 3.

    Figure 3. Setting the security level property in the COM+ Catalog

As an alternative to the manual process, you could add an attribute to your component telling it to perform access-level checks. Below is the code to add within the COM+ Registration detail section at the top of your COMPlusServices class.

<Assembly: ApplicationAccessControlAttribute
   (AccessChecksLevel:=AccessChecksLevelOption.ApplicationComponent)>  

Check for a Security Role

You will now add a new method to the class called IsManager. This method will check to see if the user is a member of the role Managers. This method is a function and returns a Boolean value indicating whether or not the caller is part of the Managers role. In order to get access to the security context of the user calling the method, you need to use the SecurityCallContext class. You get the current caller's context by calling the CurrrentCall method. Then, you call the IsCallerInRole method, and pass in Managers as the name of the role.

  1. Add the method shown below to the COMPlusServices class.

    Public Function IsManager() As Boolean
    
        Dim objCallContext As SecurityCallContext = _ 
        SecurityCallContext.CurrentCall
    
        IsManager = _ 
        objCallContext.IsCallerInRole("Managers")
    
    End Function
    

    You will now need to rebuild the component to try out this new method.

  2. From the Visual Studio .NET Build menu, click Rebuild Solution.

Try It Out

  1. Modify the code in the Sub Main() method of your console client application. Here is what the code should look like:

    Sub Main()
    
        Dim objCOMPlus As New _ 
        COMPlusJumpStart.COMPlusServices()
    
        Console.WriteLine(objCOMPlus.DoTransaction)
        Console.WriteLine(objCOMPlus.IsManager().ToString)
        Console.ReadLine()
    
    End Sub
    
  2. Run the console application from the command prompt by typing in the name of the executable you compiled.

The first time you run the code, you should get an exception stating that access was denied because no users were added to the Managers role. To correct this, add yourself as a user to Managers, and re-run the application. This time, there should not be any exceptions. You could also add exception handling to your code. Here is what your client should like with the exception handling code:

Sub Main()

Try

    Dim objCOMPlus As New _ 
    COMPlusJumpStart.COMPlusServices()

    Console.WriteLine(objCOMPlus.DoTransaction)
    Console.WriteLine(objCOMPlus.IsManager().ToString)
    Console.ReadLine()

Catch objException As Exception
    Console.WriteLine("An error occurred. " _ 
    & "Details:  " _ 
    & objException.Message)
    Console.ReadLine()

End Try

End Sub

Using Queued Components

In COM+ applications, it is easy to add queuing support. You just make sure that the application is running as a server application (out of process), and then you set its Queued and Listen properties on the Queuing tab. Once these settings are made, client applications can call your components asynchronously or synchronously. The beauty of this feature is that you do not have to change the code in your COM object; you just change its properties in the COM+ Catalog.

The .NET Framework supports queued components, and as you might expect, you can give your components queuing support by using attributes instead of manually changing the COM+ Catalog.

Let's add a method to the COMPlusServices class, and call it asynchronously using COM+ Queued Components services from a .NET client application.

  1. Make your COM+ application a server application (out of process). This is a requirement for Queued Components. To do this via attributes, add the following code to your project:

    '********************************************
    'COM+ Registration details 
    
    <Assembly: ApplicationActivationAttribute(ActivationOption.Server)>
    
  2. Add queuing support to your component. Make it accessible to MSMQ queues, and make it listen to its own queue to process messages. Here is the code to do this via attributes:

    '********************************************
    'COM+ Registration details 
    
    <Assembly: ApplicationQueuingAttribute(Enabled:=True, 
       QueueListenerEnabled:=True)>
    
  3. Add a method to your class called QueueTest. Make sure that it is a subroutine; it cannot have a return value. Have it write a message to the Windows Application Log. Here is what the code should look like:

    Public Sub QueueTest()
        System.Diagnostics.EventLog.WriteEntry(_ 
        "COMPlusServces", "Queue Test", _ 
        Diagnostics.EventLogEntryType.Error)
    End Sub
    

That's it. That is all you have to do to enable your component to be a COM+ Queued Component.

Try It Out

You should now try out this queued component by creating another console application and calling this component.

  1. Create a new console application.

  2. Add the following code to the Sub Main procedure of this console application.

    Sub Main()
        Dim objTest As COMPlusJumpStart.COMPlusServices
        Dim strMoniker
    
        strMoniker = _ 
         "queue:/new:COMPlusJumpStart.COMPlusServices"
        objTest = GetObject(strMoniker)
        objTest.QueueTest()
    End Sub
    

This asynchronously calls the QueueTest method on your component. If you wanted to call your method synchronously, you would call it like you have all of the other methods in your component.

You can now run this console application to try out this queued component.

What's Different in COM+ Since Visual Basic 6.0?

There are many things that you already know from Visual Basic 6.0 or COM that are the same in .NET. But now you have the benefit of working within the .NET Framework, which helps objects interact smoothly, with a minimum of coding and maintenance on your part.

Typical of this sort of streamlining, in Visual Basic 6.0, you had to explicitly call SetComplete and SetAbort on your own. In .NET, the SetComplete and SetAbort are called by the <AutoComplete()> attribute.

Another difference between Visual Basic .NET and Visual Basic 6.0 is that Visual Basic 6.0 components can't make use of COM+ object pooling because they use the Single Threaded Apartment threading model. Visual Basic .NET components are .NET components and as such, support the Any Apartment threading model and hence can make use of COM+ object pooling.

Summary

As you can see, the .NET Framework makes it easy to take advantage of the services that COM+ offers. This means that you can add new .NET components to existing applications written with COM and COM+, and they will be able to work together. This is very important, because it means that you do not have to throw away all of your existing COM and COM+ code. If you are building a new application from scratch, it is highly recommended that you build it entirely in the .NET Framework, as it will be much more efficient.

About the Authors

Tim McCarthy (MCSD, MCT) is a Principal Engineer at InterKnowlogy where he architects and builds multi-tiered applications utilizing the latest Microsoft technologies. He is a regular speaker at DevDays, and has just recently finished putting together .NET training content for the MSDN Field Content Team. Tim has written chapters for several Wrox Press books, the latest of which was Professional VB.NET, as well as writing several articles for SQL Server Magazine's Developer DotNET Update newsletter.

Paul D. Sheriff is the owner of PDSA, Inc., a custom software development and consulting company in Southern California. Paul is the MSDN Regional Director for Southern California, is the author of a book on Visual Basic 6 called Paul Sheriff Teaches Visual Basic, and has produced over 72 videos on Visual Basic, SQL Server, .NET and Web Development for Keystone Learning Systems. Paul has co-authored a book entitled ASP.NET Jumpstart. Visit the PDSA, Inc. Web site (www.pdsa.com) for more information.

About Informant Communications Group

Informant Communications Group, Inc. (www.informant.com) is a diversified media company focused on the information technology sector. Specializing in software development publications, conferences, catalog publishing and Web sites, ICG was founded in 1990. With offices in the United States and the United Kingdom, ICG has served as a respected media and marketing content integrator, satisfying the burgeoning appetite of IT professionals for quality technical information.

Copyright © 2002 Informant Communications Group and Microsoft Corporation

Technical editing: PDSA, Inc.