Enterprise Services (.NET 1.1) Performance Guidelines - Design Considerations
- J.D. Meier, Srinath Vasireddy, Ashish Babbar, and Alex Mackman
Use Enterprise Services Only if You Need To
Use Enterprise Services inside your service implementation when you need the component services that Enterprise Services provides. Enterprise Services provides a broad range of important infrastructure-level features for middle tier components, including distributed transaction management, object pooling, and role-based security.
Each service means more infrastructure code to execute. As a result, there is a performance overhead with using Enterprise Services so you should build serviced components and host them in Enterprise Services only if you specifically need to use the features it provides. .If you need those services, more code needs to be executed anyway, so using Enterprise Services is the right choice.
Use Library Applications if Possible
Enterprise Services provides server and library applications. Server applications run in their own process (Dllhost.exe) and use a process identity that you configure. Library applications run in their creator's process using the client's identity. Library applications offer performance benefits because they do not incur the significant marshaling overhead associated with an IPC (or cross network) call to a server application.
As such, you should use library applications whenever possible. Use server applications only if you need your components to run under a different security context from the client, or if you need them to be isolated from the client to provide additional fault tolerance.
The following code shows how to declaratively specify the activation type using an assembly level attribute.
using System.EnterpriseServices; [ assembly: ApplicationActivation(ActivationOption.Library)] public class Account : ServicedComponent { void DoSomeWork() {} }
Consider DLL and Class Relationships
If your solution includes serviced components in multiple DLLs, and there is heavy interaction between two components in separate DLLs, make sure they are located in the same Enterprise Services application. This minimizes the marshaling and security overhead associated with crossing application boundaries.
Use Distributed Transactions Only if You Need To
Enterprise Services and COM+ transactions use the services of the Microsoft Distributed Transaction Coordinator (DTC). Use DTC-based transactions if you need your transaction to span multiple remote databases or a mixture of resource manager types, such as a Microsoft SQL Server database and a Windows message queue. Also, if you need to flow transactions in a distributed application scenario, for example, across components even against a single database, consider Enterprise Services transactions.
Use Object Pooling to Reduce Object Creation Overhead
Object pooling helps minimize component activations and disposal, which can be costly compared to method calls. Consider the following recommendations:
Use object pooling if you have a component that callers use briefly and in rapid succession, and where a significant portion of the object's initialization time is spent acquiring resources or performing other initialization prior to performing specific work for the caller, configure the component to use COM+ object pooling. Use object pooling to control the maximum number of objects running at any given time. This allows you to throttle server resources because when optimum maximum value is set (best decided by trying various values and testing your application scenario), object pooling ensures that server resources are not all consumed. Avoid object pooling if you need only one object in your pool. Instead, investigate the singleton object model supported by .NET remoting.
Note that object pooling is less beneficial for objects that take a very small amount of time to initialize.
Design Pooled Objects Based on Calling Patterns
If you adopt a stateless component design and use JIT Activation, you minimize the number of active objects on the server at any given time, which means that you use the least amount of resources possible at any given time. However, this is at the expense of many activations and deactivations.
For objects that are expensive to initialize, it is best to initialize them as little as possible and enable clients to reference them between calls. For objects that retain limited, shared resources, such as database connections, it is best to free them up as soon as possible and use a stateless model with JIT Activation.
Therefore for some objects, it may be worth the cost of repetitive activations and deactivations to keep them freed up as much as possible, and for other objects it may be best to limit the activations and deactivations and keep them around.
Pooling provides a compromise for objects that are expensive to create/destroy, or for objects whose resources are expensive to acquire/release.
Use Explicit Interfaces
You should implement explicit interfaces for any serviced component that is hosted in a server application and is called from another process or computer. This allows clients to call methods on these interfaces instead of calling class methods on the default class interface. Consider the following example:
// When you create a class that is a service component, // it has a default interface public class CustomClass : ServicedComponent { public void DoSomething(); }
// instead explicitly create an interface public interface ICustomInterface { void DoSomething(); } public class CustomClass : ServicedComponent, ICustomInterface { public void DoSomething(); }
Explicit interfaces result in improved performance because there are fewer serialization costs involved. When you call an explicit interface, DCOM serialization occurs. When you call a class member directly, an additional .NET remoting serialization cost is incurred.
Design Less Chatty Interfaces
When you design interfaces, provide methods that batch arguments together to help minimize round trips. Reduce round trips by avoiding properties. For each property access, the call is marshaled to the remote object; it is intercepted for providing the services which can be relatively slow compared to grouping them into a single method call. For more information, see "Design Chunky Interfaces" in Chapter 3, "Design Guidelines for Application Performance."
Design Stateless Components
A design that avoids holding state inside components is easy to scale. Conversely, components that hold user-specific state across caller method calls cause server affinity, which limits your scalability options.
Even if your serviced component resides on the same server as your presentation layer, consider using a stateless design and take advantage of services such as JIT activation and object pooling to strike a balance between performance and scalability. This also helps if your future workload requires you to scale out.
When used correctly, object pooling can help maintain some state (such as a connection) and scale as well. Use object pooling for objects that retain connections so they can be shared by multiple clients.