/// <summary> /// Returns a connection for the current transaction. This will be an existing <see cref="DbConnection"/> /// instance or a new one if there is a <see cref="TransactionScope"/> active. Otherwise this method /// returns null. /// </summary> /// <param name="db"></param> /// <returns>Either a <see cref="DbConnection"/> instance or null.</returns> public static DatabaseConnectionWrapper GetConnection(CustomDatabase db) { Transaction currentTransaction = Transaction.Current; if (currentTransaction == null) { return(null); } Dictionary <string, DatabaseConnectionWrapper> connectionList; DatabaseConnectionWrapper connection; lock (transactionConnections) { if (!transactionConnections.TryGetValue(currentTransaction, out connectionList)) { // We don't have a list for this transaction, so create a new one connectionList = new Dictionary <string, DatabaseConnectionWrapper>(); transactionConnections.Add(currentTransaction, connectionList); // We need to know when this previously unknown transaction is completed too currentTransaction.TransactionCompleted += OnTransactionCompleted; } } lock (connectionList) { // Next we'll see if there is already a connection. If not, we'll create a new connection and add it // to the transaction's list of connections. // This collection should only be modified by the thread where the transaction scope was created // while the transaction scope is active. // However there's no documentation to confirm this, so we err on the safe side and lock. if (!connectionList.TryGetValue(db.ConnectionString, out connection)) { // we're betting the cost of acquiring a new finer-grained lock is less than // that of opening a new connection, and besides this allows threads to work in parallel var dbConnection = db.GetNewOpenConnection(); connection = new DatabaseConnectionWrapper(dbConnection); connectionList.Add(db.ConnectionString, connection); } connection.AddRef(); } return(connection); }
/// <summary> /// Create a new <see cref='RefCountingDataReader'/> that wraps /// the given <paramref name="innerReader"/> and properly /// cleans the refcount on the given <paramref name="connection"/> /// when done. /// </summary> /// <param name="connection">Connection to close.</param> /// <param name="innerReader">Reader to do the actual work.</param> public RefCountingDataReader(DatabaseConnectionWrapper connection, IDataReader innerReader) : base(innerReader) { _connectionWrapper = connection; _connectionWrapper.AddRef(); }
protected virtual IDataReader CreateWrappedReader(DatabaseConnectionWrapper connection, IDataReader innerReader) { return(new RefCountingDataReader(connection, innerReader)); }