Skip to content

Commit 3a307a0

Browse files
authored
Merge pull request #1 from fredericDelaporte/tableAlias
Clear pool for all Oracle drivers.
2 parents 7694de0 + 3f7593d commit 3a307a0

File tree

7 files changed

+108
-38
lines changed

7 files changed

+108
-38
lines changed

src/NHibernate.Test/TestCase.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313
using NUnit.Framework;
1414
using NUnit.Framework.Interfaces;
1515
using System.Text;
16-
using NHibernate.Dialect;
1716
using NHibernate.Driver;
18-
using Oracle.ManagedDataAccess.Client;
1917

2018
namespace NHibernate.Test
2119
{
@@ -314,16 +312,24 @@ protected virtual DebugSessionFactory BuildSessionFactory()
314312

315313
private void Cleanup()
316314
{
317-
Sfi?.Close();
318-
319315
// Clear connection pool for Oracle to avoid problem that was manifested with https://github.com/nhibernate/nhibernate-core/pull/1517:
320316
// As it seems Oracle can cache returned types for query for given connection.
321317
// So exception can be thrown if two tests execute same query but with different types in result (like for Entity.Id int and Entity.Id Guid)
322-
if (Dialect is Oracle8iDialect)
318+
switch (Sfi?.ConnectionProvider.Driver)
323319
{
324-
OracleConnection.ClearAllPools();
320+
case OracleClientDriver oraSysData:
321+
oraSysData.ClearPool(null);
322+
break;
323+
case OracleDataClientDriver oraUnmanaged:
324+
oraUnmanaged.ClearPool(null);
325+
break;
326+
case OracleManagedDataClientDriver oraManaged:
327+
oraManaged.ClearPool(null);
328+
break;
325329
}
326330

331+
Sfi?.Close();
332+
327333
_sessionFactory = null;
328334
cfg = null;
329335
}

src/NHibernate/Driver/FirebirdClientDriver.cs

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
using System;
21
using System.Collections.Generic;
32
using System.Data;
43
using System.Data.Common;
54
using System.Linq;
6-
using System.Reflection;
75
using System.Text.RegularExpressions;
86
using NHibernate.Dialect;
97
using NHibernate.SqlCommand;
@@ -128,39 +126,16 @@ private string GetFbTypeForParam(SqlType sqlType)
128126
return _fbDialect.GetCastTypeName(sqlType);
129127
}
130128

131-
private static volatile MethodInfo _clearPool;
132-
private static volatile MethodInfo _clearAllPools;
133-
134129
/// <summary>
135130
/// Clears the connection pool.
136131
/// </summary>
137132
/// <param name="connectionString">The connection string of connections for which to clear the pool.
138133
/// <c>null</c> for clearing them all.</param>
139134
public void ClearPool(string connectionString)
140135
{
141-
// In case of concurrent threads, may initialize many times. We do not care.
142-
// Members are volatile for avoiding it gets used while its constructor is not yet ended.
143-
if (_clearPool == null || _clearAllPools == null)
144-
{
145-
using (var clearConnection = CreateConnection())
146-
{
147-
var connectionType = clearConnection.GetType();
148-
_clearPool = connectionType.GetMethod("ClearPool") ?? throw new InvalidOperationException("Unable to resolve ClearPool method.");
149-
_clearAllPools = connectionType.GetMethod("ClearAllPools") ?? throw new InvalidOperationException("Unable to resolve ClearAllPools method.");
150-
}
151-
}
152-
153-
if (connectionString != null)
154-
{
155-
using (var clearConnection = CreateConnection())
156-
{
157-
clearConnection.ConnectionString = connectionString;
158-
_clearPool.Invoke(null, new object[] {clearConnection});
159-
}
160-
return;
161-
}
162-
163-
_clearAllPools.Invoke(null, Array.Empty<object>());
136+
// Do not move in a base class common to different connection types, or it may not clear
137+
// expected pool.
138+
PoolHelper<FirebirdClientDriver>.ClearPool(this, connectionString);
164139
}
165140

166141
/// <summary>

src/NHibernate/Driver/OracleClientDriver.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,17 @@ protected override void OnBeforePrepare(DbCommand command)
5959
" does not support CallableStatement syntax (stored procedures)." +
6060
" Consider using OracleDataClientDriver instead.");
6161
}
62+
63+
/// <summary>
64+
/// Clears the connection pool.
65+
/// </summary>
66+
/// <param name="connectionString">The connection string of connections for which to clear the pool.
67+
/// <c>null</c> for clearing them all.</param>
68+
public void ClearPool(string connectionString)
69+
{
70+
// Do not move in a base class common to different connection types, or it may not clear
71+
// expected pool.
72+
PoolHelper<OracleClientDriver>.ClearPool(this, connectionString);
73+
}
6274
}
63-
}
75+
}

src/NHibernate/Driver/OracleDataClientDriver.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,17 @@ public OracleDataClientDriver()
1515
: base("Oracle.DataAccess")
1616
{
1717
}
18+
19+
/// <summary>
20+
/// Clears the connection pool.
21+
/// </summary>
22+
/// <param name="connectionString">The connection string of connections for which to clear the pool.
23+
/// <c>null</c> for clearing them all.</param>
24+
public void ClearPool(string connectionString)
25+
{
26+
// Do not move in a base class common to different connection types, or it may not clear
27+
// expected pool.
28+
PoolHelper<OracleDataClientDriver>.ClearPool(this, connectionString);
29+
}
1830
}
19-
}
31+
}

src/NHibernate/Driver/OracleDataClientDriverBase.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using System.Data;
44
using System.Data.Common;
5-
using System.Reflection;
65
using NHibernate.AdoNet;
76
using NHibernate.Engine.Query;
87
using NHibernate.SqlTypes;

src/NHibernate/Driver/OracleManagedDataClientDriver.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,17 @@ public OracleManagedDataClientDriver()
1717
}
1818

1919
public override bool HasDelayedDistributedTransactionCompletion => true;
20+
21+
/// <summary>
22+
/// Clears the connection pool.
23+
/// </summary>
24+
/// <param name="connectionString">The connection string of connections for which to clear the pool.
25+
/// <c>null</c> for clearing them all.</param>
26+
public void ClearPool(string connectionString)
27+
{
28+
// Do not move in a base class common to different connection types, or it may not clear
29+
// expected pool.
30+
PoolHelper<OracleManagedDataClientDriver>.ClearPool(this, connectionString);
31+
}
2032
}
2133
}

src/NHibernate/Driver/ReflectionBasedDriver.cs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Data.Common;
3+
using System.Reflection;
34
using NHibernate.Util;
45

56
namespace NHibernate.Driver
@@ -70,5 +71,58 @@ public override DbCommand CreateCommand()
7071
{
7172
return connectionCommandProvider.CreateCommand();
7273
}
74+
75+
/// <summary>
76+
/// Helper for clearing connection pools used by a reflection driver. Assumes the connection has a parameter-less
77+
/// <c>ClearAllPools</c> method and a <c>ClearPool</c> method taking as argument a connection.
78+
/// </summary>
79+
/// <typeparam name="T">The driver type for which the pool has to be cleared. This driver type must
80+
/// always use the same connection type.</typeparam>
81+
/// <remarks>Having <c>ClearAllPools</c> and <c>ClearPool</c> is a common pattern. <c>SqlConnection</c>,
82+
/// <c>OracleConnection</c> (managed, un-managed and from <c>System.Data</c>), <c>FirebirdConnection</c>
83+
/// <c>NpgsqlConnection</c>, <c>MySqlConnection</c> and <c>SQLiteConnection</c> have them.
84+
/// (<c>SqlCeConnection</c>, <c>OdbcConnection</c> and <c>OleDbConnection</c> lack them.)</remarks>
85+
protected static class PoolHelper<T> where T : IDriver
86+
{
87+
// Static field in generic class => one field per concrete type used. This is exactly what
88+
// we need here, do not move the generic argument to the method. Otherwise it will cache the
89+
// method info of the first driver type used, and reuse it for other driver types, which
90+
// would fail.
91+
private static volatile MethodInfo _clearPool;
92+
private static volatile MethodInfo _clearAllPools;
93+
94+
/// <summary>
95+
/// Clears the connection pool.
96+
/// </summary>
97+
/// <param name="driver">The driver for which the connection pool has to be cleared.</param>
98+
/// <param name="connectionString">The connection string of connections for which to clear the pool.
99+
/// <c>null</c> for clearing them all.</param>
100+
internal static void ClearPool(T driver, string connectionString)
101+
{
102+
// In case of concurrent threads, may initialize many times. We do not care.
103+
// Members are volatile for avoiding they get used while their constructor is not yet ended.
104+
if (_clearPool == null || _clearAllPools == null)
105+
{
106+
using (var clearConnection = driver.CreateConnection())
107+
{
108+
var connectionType = clearConnection.GetType();
109+
_clearPool = connectionType.GetMethod("ClearPool") ?? throw new InvalidOperationException("Unable to resolve ClearPool method.");
110+
_clearAllPools = connectionType.GetMethod("ClearAllPools") ?? throw new InvalidOperationException("Unable to resolve ClearAllPools method.");
111+
}
112+
}
113+
114+
if (connectionString != null)
115+
{
116+
using (var clearConnection = driver.CreateConnection())
117+
{
118+
clearConnection.ConnectionString = connectionString;
119+
_clearPool.Invoke(null, new object[] {clearConnection});
120+
}
121+
return;
122+
}
123+
124+
_clearAllPools.Invoke(null, Array.Empty<object>());
125+
}
126+
}
73127
}
74-
}
128+
}

0 commit comments

Comments
 (0)