|
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Data.Common;
using SubSonic.Utilities;
namespace SubSonic
{
/// Big thanks to Brenton Webster the code contained herein...
/// <summary>
/// Indicates that a per-thread shared DbConnection object should be used the default DataProvider
/// (or alternativley a specific DataProvider if one is given) when communicating with the database.
///
/// This class is designed to be used within a using () {} block and in conjunction with a TransactionScope object.
/// It's purpose is to force a common DbConnection object to be used which has the effect of avoiding promotion
/// of a System.Transaction ambient Transaction to the DTC where possible.
///
/// When this class is created, it indicates to the underlying DataProvider that is should use a shared DbConnection
/// for subsequent operations. When the class is disposed (ie the using() {} block ends) it will indicate to the
/// underlying provider that it should no longer it's current shared connection and should Dispose() it.
/// </summary>
public class SharedDbConnectionScope
: IDisposable
{
private bool _disposed;
private DataProvider _dataProvider;
/// <summary>
/// Used to support nesting. By keeping a stack of all instances of the class that are created on this thread
/// thread we know when it is safe to Reset the underlying shared connection.
/// </summary>
[ThreadStatic]
private static Stack<SharedDbConnectionScope> __instances;
/// <summary>
/// Provides access to underlying connection that is shared per thread
/// </summary>
public DbConnection CurrentConnection
{
get { return _dataProvider.CurrentSharedConnection; }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// remove this instance from the stack
__instances.Pop();
// if we are the last instance, reset the connection
if (__instances.Count == 0)
_dataProvider.ResetSharedConnection();
_disposed = true;
}
}
}
/// <summary>
/// Indicates to the default DataProvider that it should use a per-thread shared connection.
/// </summary>
public SharedDbConnectionScope()
: this(DataService.Provider)
{
}
/// <summary>
/// Indicates to the default DataProvider that it should use a per-thread shared connection using the given connection string.
/// </summary>
/// <param name="connectionString"></param>
public SharedDbConnectionScope(string connectionString)
: this(DataService.Provider, connectionString)
{
}
/// <summary>
/// Indicates to the specified DataProvider that it should use a per-thread shared connection.
/// </summary>
/// <param name="dataProvider"></param>
public SharedDbConnectionScope(DataProvider dataProvider)
{
if (dataProvider == null)
throw new ArgumentNullException("dataProvider");
_dataProvider = dataProvider;
_dataProvider.InitializeSharedConnection();
if (__instances == null)
__instances = new Stack<SharedDbConnectionScope>();
__instances.Push(this);
}
/// <summary>
/// Indicates to the specified DataProvider that it should use a per-thread shared connection using the given connection string.
/// </summary>
/// <param name="dataProvider"></param>
/// <param name="connectionString"></param>
public SharedDbConnectionScope(DataProvider dataProvider, string connectionString)
{
if (dataProvider == null)
throw new ArgumentNullException("dataProvider");
if (connectionString == null)
throw new ArgumentNullException("connectionString");
if (String.IsNullOrEmpty(connectionString))
throw new ArgumentException("connectionString can not be empty");
_dataProvider = dataProvider;
_dataProvider.InitializeSharedConnection(connectionString);
if (__instances == null)
__instances = new Stack<SharedDbConnectionScope>();
__instances.Push(this);
}
}
/// <summary>
/// Used within SubSonic to automatically manage a SqlConnection. If a shared connection is available
/// for the specified provider on the current thread, that shared connection will be used.
/// Otherwise, a new connection will be created.
/// Note that if a shared connection is used, it will NOT be automatically disposed - that is up to the caller.
/// Lifetime management of the shared connection is taken care of by using a <see cref="SharedDbConnectionScope"/>
/// If a new connection is created, it will be automatically dsiposed when this AutomaticConnectionScope object
/// is disposed.
/// </summary>
internal class AutomaticConnectionScope
: IDisposable
{
private DbConnection _dbConnection;
private bool _isUsingSharedConnection;
private bool _disposed;
internal DbConnection Connection
{
get { return _dbConnection; }
}
internal bool IsUsingSharedConnection
{
get { return _isUsingSharedConnection; }
}
internal AutomaticConnectionScope(DataProvider provider)
{
if (provider == null)
throw new ArgumentNullException("provider");
if (provider.CurrentSharedConnection != null)
{
_dbConnection = provider.CurrentSharedConnection;
_isUsingSharedConnection = true;
}
else
_dbConnection = provider.CreateConnection();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// only dispose the connection if it is not a shared one
if (!_isUsingSharedConnection)
_dbConnection.Dispose();
_disposed = true;
}
}
}
public T GetConnection<T>()
where T : DbConnection
{
return (T)Connection;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.