In-Depth

Enlist ADO.NET Connections in Pooled Objects Manually

You can boost performance in server-side apps by using COM+ pooled objects to cache resources. Be sure to enlist the connection manually each time the object pool picks up the object.

Technology Toolbox: C#, SQL Server 2000

Using COM+ pooled objects to cache and reuse valuable resources is one of the most effective ways to boost performance in server-side .NET and COM-based applications. Database connections are among the most precious server-side resources, so they've always been the number-one candidate for pooling. The OLE DB infrastructure offers database connection pooling features, but many performance tests have shown that caching database connections wrapped inside COM+ pooled objects manually outperforms OLE DB-based connection pooling.

Developing COM+ pooled objects was out of reach for VB6 developers, but implementing .NET COM+ pooled objects (using the Enterprise Services infrastructure) has become a snap. However, you must be aware of a big gotcha when caching connections in a pooled object when you're using Distributed Transaction Coordinator (DTC) based transactions. If a serviced component (one using Enterprise Services) runs inside a COM+ transactional context, database connections are enlisted automatically when they're opened or picked up from the database connection pool. This means that cached connections created and opened in the class constructor won't magically enlist into the active transactional context each time a call enters a pooled object's method.

You need to enlist the connection into the active transactional context manually each time the object is picked up from the object pool. You can hook into this event overriding the ServicedComponent Activate method. You extract the active transactional context and attach it to the database connection using the EnlistDistributedTransaction method that all connection classes (no matter what managed provider you're using) expose:

protected override void Activate() {
	// m_con is a DB connection that has been
	// opened in the class's constructor phase 
	m_con.EnlistDistributedTransaction 
		((ITransaction)ContextUtil.Transaction);
		base.Activate();
}

This is all you need to do, but it's good coding practice to set the Enlist=false parameter in the database connection string:

[Transaction(TransactionOption.Required), 
	ObjectPooling(true,3,30),
public class myclass : ServicedComponent {
	private SqlConnection m_con ;
	public myclass(){
		m_con = new  SqlConnection( 
			"Password=whatever;User ID=sa;Initial
			Catalog=pubs;Enlist=false")
		// the DB connection is opened when the object 
		// is created 
		m_con.Open ();
}

Un-enlist the connection from the transaction in the Deactivate event—that is, when the object is going to be put back in the pool:

protected override void Deactivate() {
	m_con.EnlistDistributedTransaction (null);
	base.Deactivate ();
}

Note, however, that these last two actions are not required at present.

One last thing: Each time the object is deactivated, the COM+ runtime calls the CanBePooled method of the ServicedComponent class to check if the object must be set back in the pool or destroyed. You must override this method in your class because the implementation of the base class always returns false, making your whole pooling strategy ineffective:

protected override bool CanBePooled() {
	// this object can always be pooled
	return true;
}

The connection pooling mechanism of the SqlClient managed provider in the .NET Framework has been rewritten internally using COM+ pooling features. You might not see significant improvements when caching SqlConnections, but if you're still using the OLE DB provider, you'd better roll your own connection caching mechanism using pooled objects if you want to squeeze every last drop of performance.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube