I've developed a reputation for constructive ideas
and criticism around our developer group at work, and recently as part
of an overall effort to help optimize our "collective code",
I had the chance to uphold that reputation a bit by distributing some
code review comments to key members of our group. I had been reviewing
some of the component source code, as well as some of the professional
documentation regarding COM+, and I'd like to share a few of the suggestions
I made for improving the performance and scalability of some the components
we had been working on:
1) First, and most important, is that if you are writing
Component Services - compliant COM components that will be hosted in COM+
Applications, you should use ObjectControl_Activate and
instead of Class_Initialize and Terminate in all classes. This holds
true whether or not the component initializes or is enlisted in transactions.
The Class_Initialize event happens before the object
is put under COM+ activity control. This can have undesirable consequences
that can make components difficult to debug. When an object is created
to run in COM+ there is an internal structure called context for
it that encapsulates info such as transaction enlistment, threading model
and so on. When the Class_Initialize event runs, the context has not yet
been associated with the object.
We do this by adding Implements ObjectControl
at the top of the class module. Move all code (if possible) from Class_Initialize
to ObjectControl_Activate, and from Class_Terminate to ObjectControl_Deactivate.
I have built a small demo COM+ component along with a
VB executable "Test harness" to illustrate the method, as well
as to pop up some messageboxes on the firing of each of the above-captioned
events so that you can get a feel for how these events are carried out
when a class method is invoked under COM+. Compile the DLL portion of
the project group and drop the DLL into a COM+ Application. Then, run
the Test Harness VB executable which is marked as the Start Up project
in the project group. You'll find this code in the download zip linked
at the bottom of this article.
2) Early binding should be used wherever possible.
If your code has instances of "Dim XYX
as OBJECT", that's late binding. It's important to note that this
has nothing to do with the operator used to instantiate the object (e.g.,
CreateInstance, CreateObject or New). In all cases, early binding requires
you to have the correct references set in the VB Project. The reason why
we want to do this is that late-bound calls are done through the IDispatch
interface, which requires two calls to execute a specific method.
3). Declare Parameters as ByVal whenever possible. Above all other
transgressions, this is the most common blooper I have found. VB
defines parameters by Reference by default. By using the ByVal keyword
when declaring a parameter, the parameter doesn't get transmitted back
to the caller using the network unnecessarily. Also, if the client does
not require the data to be sent back, passing in the parameter as ByVal
matches the interface's semantics more closely. In other words, except
for specific instances where a function is supposed to actually MODIFY
THE VALUE of a variable that is passed in as a parameter (rather than
returning a RESULT based on what the method does), we should be passing
parameters specifically coded as "ByVal".
4). Do not declare variables "AS NEW".
Declaring variables using "as new xyz"
causes every reference to be slower, and can also have an unpredictable
effect on the object lifetime. In addition, when used in combination with
private classes, it can bring unexpected thread behavior. Using
"as new" causes VB to compile the component with extra checks
to see if the object has been set to Nothing. If not, VB will create
a new object EVERY TIME the variable is used!
Objects should be declared and created in distinct steps.
It is important to use the correct operator to create objects:
To create utility objects or objects such as ADO,
To create custom objects in other project or DLL
To create custom objects in the same DLL
To create objects by a runtime-specified ProgID and / or computer.
Examples of correct usage:
Set oTransfer = New OtherProject.CTransfer
Set oConn = New ADODB.Connection
Creating an object in the same DLL (COM+):
Set oISPSubs = CreateObject("MyProject.MyClass")
5) State Management
Tthe ASP Session object is a big convenience, but
it's also a real drag on performance. I've written a state management
that works with SQL Server. It is very fast, has been fully stress-tested
and is 100% UNICODE compliant. It's syntax is virtually identical to the
intrinsic ASP Session object. Either this, or some similar component should
be used, and ASP Session state can be TURNED OFF on IIS, thereby speeding
up web server(s) and reducing the load on the IIS machine(s). On a web
farm, this is not a luxury, its a necessity. Session State is best stored
in SQL Server, even if you decide to cache it somewhere else later.
I hope these few suggestions have either presented some
logical new information, or have helped to "jog your memory"
about things you already knew you should do.
Download the code that accompanies this article
Peter Bromberg is an independent consultant specializing in distributed .NET solutionsa Senior Programmer
/Analyst at in Orlando and a co-developer of the NullSkull.com
developer website. He can be reached at firstname.lastname@example.org