Is there a standard connection pooling model
Other than ADO.NET, no. But the ADO.NET model is nice an simple. Construct an object to get a connection from the pool, or created anew, and it is returned to the pool on Close/Dispose/Finalise.
From this one can immediately determine an implementation pattern:
- The client type is a proxy to the real type, and has a lifetime from creation to
Close/…. It is a proxy to the real object. Provides methods and properties which
forward to the real connection.
- The real connection is a long lived instance, created by the pool, given out under a proxy
and then returned at the end of the proxy.
There is a choice in the implementation. When an object has been handed out, does the pool need to also keep a reference? If it does the pool needs to track which objects are active and which are pooled; otherwise a simple collection of available objects can be used.
Something like:
internal class MyObjectImpl {
// The real object that holds the resource
}
internal static class MyObjectPool {
private static object syncRoot = new object();
private static Queue<MyObjectImpl> pool = new Queue<MyObject>();
private static int totalObjects = 0;
private readonly int maxObjects = 10;
internal MyObjectImplGet() {
lock (syncRoot) {
if (pool.Count > 0) {
return pool.Dequeue();
}
if (totalObjects >= maxObjects) {
throw new PoolException("No objects available");
}
var o = new MyObjectImpl();
totalObjects++;
return o;
}
}
internal void Return(MyObjectImpl obj) {
lock (syncRoot) {
pool.Enqueue(obj);
}
}
}
public class MyObject : IDisposable {
private MyObjectImpl impl;
public MyObject() {
impl = MyObjectPool.Get();
}
public void Close() {
Dispose();
}
public void Dispose() {
MyIObjectPool.Return(impl);
// Prevent continuing use, as the implementation object instance
// could now be given out.
impl = null;
}
// Forward API to implement
}
This doesn't cater for instances of MyObject
being destroyed. E.g. hold a collection of weak references to allocated MyObject
's, and if the pool is empty check for disposed instances. This would also be needed if you cannot rely on client's to Close or dispose of instances, or implement a finaliser on MyObjectImpl
1 (and report this as an error in debug builds).
1 This cannot be done on MyObject because by the time MyObject was finalised the MyObjectImpl instance could already have been finalised.