Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training,
learning paths, books, tutorials, and more.

COM+ Services in .NET

COM programming requires lots of housekeeping
and infrastructure-level code to build large-scale, enterprise
applications. Making it easier to develop and deploy transactional
and scalable COM applications, Microsoft
released Microsoft
Transaction Server (MTS). MTS allows you to share resources, thereby
increasing the scalability of an application. COM+ Services were the
natural evolution of MTS. While MTS was just another library on top
of COM, COM+ Services were subsumed into the COM library, thus
combining both COM and MTS into a single runtime.

COM+ Services have been very valuable to the development shops using
the COM model to build applications that take advantage of
transactions, object pooling, role-based security, and so forth. If
you develop enterprise .NET applications, the COM+ Services in .NET
are a must.

In the following examples, rather than feeding you more principles,
we’ll show you examples for using major COM+ Services in .NET,
including examples on transactional programming, object pooling, and
role-based security. But before you see these examples, let’s
talk about the key element—attributes—that enable the use
of these services in .NET.

Attribute-Based Programming

Attributesare the key elements that help you
write less code and allow an infrastructure to automatically inject
the necessary code for you at runtime. If you’ve used
IDL (Interface
Definition Language) before, you have seen the in
or out attributes, as in the following example:

HRESULT SetAge([in] short age);
HRESULT GetAge([out] short *age);

IDL allows you to add these attributes so that the marshaler will
know how to optimize the use of the network. Here, the
in attribute tells the marshaler to send the
contents from the client to the server, and the
out attribute tells the marshaler to send the
contents from the server to the client. In the SetAge( ) method,
passing age from the server to the client will
just waste bandwidth. Similarly, there’s no need to pass
age from the client to the server in the GetAge( )
method.

Developing custom attributes

Whilein and
out are built-in attributes that the MIDL
compiler supports, .NET allows you to create your own custom
attributes by deriving from the System.Attribute class. Here’s
an example of a custom attribute:

The AttributeUsage attribute that we’ve applied to our
AuthorAttribute class specifies the rules for using
AuthorAttribute.[29] Specifically, it says that
AuthorAttribute can prefix or describe a class or
any class member.

Using custom attributes

Given that we have this attribute, we can write a simple class to
make use of it. To apply our attribute to a class or a member, we
simply make use of the attribute’s available constructors. In
our case, we have only one and it’s AuthorAttribute( ), which
takes an author’s skill level. While you can use
AuthorAttribute( ) to instantiate this attribute, .NET allows you to
drop the Attribute suffix for convenience, as
shown in the following code listing:

You’ll notice that we’ve applied the Author attribute to
the Customer class, telling the world that a guru wrote this class
definition. This code also shows that a senior programmer wrote the
Add( ) method and that a junior programmer wrote the Delete( )
method.

Inspecting attributes

You will not see the full benefits of attributes until you write a
simple interceptor-like program, which looks for special attributes
and provides additional services appropriate for these attributes.
Examples of real interceptors are marshaling, transaction, security,
pooling, and other services in MTS and COM+.

Here’s a simple interceptor-like program that uses the
Reflection API to look for AuthorAttribute and provide additional
services. You’ll notice that we can ask a type,
Customer in this case, for all of its custom
attributes. In our code, we ensure that the Customer class has
attributes and that the first attribute is
AuthorAttribute before we output the appropriate
messages to the console. In addition, we look for all members that
belong to the Customer class and check whether they have custom
attributes. If they do, we ensure that the first attribute is an
AuthorAttribute before we output the appropriate
messages to the console.

It is crucial to note that when this program sees a piece of code
written by a junior programmer, it automatically performs a rigorous
review of the code. If you compile and run this program, it will
output the following to the console:

Class [Customer], written by a Guru programmer.
Method [Add], written by a Senior programmer.Method [Delete], written by a Junior programmer.***Performing automatic review of Junior's code***

While our interceptor-like program doesn’t intercept any
object-creation and method invocations, it does show how a real
interceptor can examine attributes at runtime and provide necessary
services stipulated by the attributes. Again, the key here is the
last boldface line, which represents a special service that the
interceptor provides as a result of attribute inspection.

Transactions

It is elementary to write a .NET class
to take advantage of the transaction support that COM+ Services
provide. All you need to supply at development time are a few
attributes, and your .NET components are automatically registered
against the COM+ catalog the first time they are used. Put
differently, not only do you get easier programming, but you also get
just-in-time and automatic registration of your COM+
application.[30] To develop
a .NET class that supports transactions, here’s what must
happen:

Your class must derive from the ServicedComponent class to exploit COM+
Services.

You must describe your class with the correct Transaction
attribute, such as
Transaction(TransactionOption.Required), meaning that instances of
your class must run within a transaction.

Besides these two requirements, you can use the ContextUtil class
(which is a part of the System.EnterpriseServices namespace) to
obtain information about the COM+ object context. This class exposes
the major functionality found in COM+, including methods such as
SetComplete( ), SetAbort( ), and
IsCallerInRole( ), and properties such as
IsInTransaction and
MyTransactionVote.

In addition, while it’s not necessary to specify any COM+
application installation options, you should do so because you get to
specify what you want, including the name of your COM+ application,
its activation setting, its versions, and so on. For example, in the
following code listing, if you don’t specify the
ApplicationName attribute, .NET will use the module name as the COM+
application name, displayed in the Component Services Explorer (or COM+
Explorer). For example, if the name of module is
crm.dll, the name of your COM+ application will
be crm. Other than this attribute, we also use the
ApplicationActivation attribute to
specify that this component will be installed as a library
application, meaning that the component will be activated in the
creator’s process.

The rest should look extremely familiar. In the
Add( ) method,
we simply call SetComplete( ) when we’ve successfully added the
new customer into our databases. If something has gone wrong during
the process, we will vote to abort this transaction by calling
SetAbort( ).

Instead of calling SetComplete( ) and SetAbort( ) yourself, you can
also use the AutoComplete attribute, as in the following code, which
is conceptually equivalent to the previously shown Add( ) method:

[AutoComplete]
public void Add(string strName)
{
Console.WriteLine("New customer: {0}", strName);
// Add the new customer into the system,
// and make appropriate updates to
// several databases.
}

Here’s how you build this assembly:

csc /t:library /out:crm.dll crm.cs

Since this is a shared assembly, remember to register it against the
GAC by using the GAC utility:

gacutil /i crm.dll

At this point, the assembly has not been registered as a COM+
application, but we don’t need to register it manually.
Instead, .NET automatically registers and hosts this component for us
in a COM+ application the first time we use this component. So,
let’s write a simple client program that uses this component at
this point. As you can see in the following code, we instantiate a
Customer object and add a new customer:

When we run this application, COM+ Services automatically create a
COM+ application called .NETEssentialsCRM to host our
crm.dll .NET assembly, as shown in Figure 4-5. In addition to adding our component to the
created COM+ application, .NET also inspects our metadata for
provided attributes and configures the associated services in the
COM+ catalog.

Figure 4-5. The Component Services Explorer

As you can see, developing transactional components in .NET is quite
easy.

Object Pooling

Apool is technical term that refers to a group of
resources, such as connections, threads, and objects. Putting a few
objects into a pool allows hundreds of clients to share these few
objects (you can make the same assertion for threads, connections,
and other objects). Pooling is therefore a technique that minimizes
the use of system resources, improves performance, and helps system
scalability.

Missing in MTS, object pooling is a nice
feature in COM+ that allows you to pool objects that support
transactions but are expensive to create. Similar to providing
support for transactions, if you want to support object pooling in a
.NET class, you need to derive from ServicedComponent, override any
of the Activate( ), Deactivate( ), and
CanBePooled( ) methods, and specify the object-pooling requirements
in an ObjectPooling attribute, as shown in the following
example:[31]

Take advantage of the Activate( ) and Deactivate( ) methods to perform
appropriate initialization and cleanup. The CanBePooled( ) method
lets you tell COM+ whether your object can be pooled when this method
is called. You need to provide the expensive object-creation
functionality in the constructor, as shown in the constructor of this
class.

Given this Customer class that supports both transaction and object
pooling, you can write the following client-side code to test object
pooling. For brevity, we will create only two objects, but you can
change this number to anything you like so that you can see the
effects of object pooling. Just to ensure that you have the correct
configuration, delete the current .NETEssentialsCRM COM+ application
from the Component Services Explorer before running the following
code:

We’ve created two objects, but since we’ve used object
pooling, only one object is really needed to support our calls, and
that’s why you see only one output statement that says,
Someexpensiveobjectconstruction. In this
case, COM+ creates only one Customer object, but activates and
deactivates it twice to support our two calls. After each call, it
puts the object back into the object pool. When a new call arrives,
it picks the same object from the pool to service the request.

Role-Based Security

Role-based security in MTS and COM+ has
drastically simplified the development and configuration of security
for business applications. This is because it abstracts away the
complicated details for dealing with access control lists
(ACL) and security identifiers (SID). All
.NET components that are hosted in a COM+ application can take
advantage of role-based security. You can fully configure role-based
security using the Component Services Explorer, but you can also
manage role-based security in your code to provide fine-grain
security support that’s missing from the Component Services
Explorer.

Configuring role-based security

In order to demonstrate role-based security, let’s add two
roles to our COM+ application, .NETEssentialsCRM. The first role
represents Agents
who can use the Customer class in every way but can’t delete
customers. You should create this role and add to it the local
Users group, as shown in Figure 4-6. The second role represents
Managers who can use the Customer class in every
way, including deleting customers. Create this role, and add to it
the local Administrators group.

Figure 4-6. Creating roles and adding users to roles

Once you’ve created these roles, you need to enable access
checks for the .NETEssentialsCRM COM+ application. Launch the COM+ application’s
Properties sheet (by selecting .NET Essentials CRM
and pressing Alt-Enter), and select the Security tab. Enable access
checks to your COM+ application by providing the options as shown in
Figure 4-7.

Figure 4-7. Enable authorization for this COM+ application

Once you have enabled access checks at the application level, you
need to enforce access checks at the class level too. To do this,
launch Customer’s Properties sheet, and
select the Security tab. Enable access checks to this .NET class by
providing the options shown in Figure 4-8. Here,
we’re saying that no one can access the Customer class except
for those that belong to the Manager or Agent role.

Figure 4-8. Enforce class-level access checks

Now, if you run the client application developed in the last section,
everything will work because you are a user on your machine. But if
you uncheck both the Manager[32] and
Agent roles in Figure 4-8 and
rerun the client application, you get the following message as part
of your output:

You’re getting this exception because you’ve removed
yourself from the roles that have access to the Customer class. Once
you’ve verified this, put the configuration back to what is
shown in Figure 4-8 to prepare the environment for
the next test that we’re about to illustrate.

Programming role-based security

We’ve allowed
anyone in the Agent and Manager
roles to access our class, but let’s invent a rule allowing
only users under the Manager role to delete a
customer from the system (for lack of a better example). So
let’s add a new method to the Customer class—we’ll
call this method Delete( ), as shown in the following
code. Anyone belonging to the Agent or
Manager role can invoke this method, so
we’ll first output to the console the user account that invokes
this method. After doing this, we’ll check to ensure that this
user belongs to the Manager role. If so, we allow
the call to go through; otherwise, we throw an exception indicating
that only managers can perform a deletion. Believe it our not, this
is the basic premise for programming role-based security.

Once you’ve built this program, you can test it using an
account that belongs to the local Users group,
since we added this group to the Agent role
earlier. On Windows 2000, you can use the
following command to launch a command window using a specific
account:

runas /user:dog\thuant cmd

Of course, you should replace dog and
thuant with your own machine name and user
account, respectively. After running this command, you will need to
type in the correct password, and a new command window will appear.
Execute the client under this user account, and you’ll see the
following output:

Add customer: John OsbornCaller: DOG\thuantSystem.Exception: Only managers can delete customers.
at Customer.Delete(String strName)

You’ll notice that the Add( ) operation went through
successfully, but the Delete( ) operation failed because we executed
the client application under an account that’s missing from the
Manager role.

To remedy this, we need to use a user account that belongs to the
Manager role—any account that belongs to the
Administrators group will do. So, start another
command window using the following command:

runas /user:dog\administrator cmd

Execute the client application again, and you’ll get the
following output:

As you can see, since we’ve executed the client application
using an account that belongs to the Manager role,
the Delete( ) operation went through
without problems.

[29] You don’t have to postfix your
attribute class name with the word Attribute, but
this is a standard naming convention that Microsoft uses. C# lets you
name your attribute class any way you like; for example,
Author is a valid class name for your
attribute.

[30] Automatic registration is nice during
development, but don’t use this feature in a production
environment, because not all clients will have the administrative
privilege to set up COM+ applications.

[32] Since
you’re a developer, you’re probably an administrator on
your machine, so you need to uncheck the Manager
role too in order to see an access violation in the test that
we’re about to illustrate.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training,
learning paths, books, interactive tutorials, and more.