using System; using System.Collections.Concurrent; using System.Data.Common; using System.Linq.Expressions; using System.Reflection; namespace Dapper.ProviderTools { /// /// Helper utilities for working with database connections /// public static class DbConnectionExtensions { /// /// Attempt to get the client connection id for a given connection /// public static bool TryGetClientConnectionId(this DbConnection connection, out Guid clientConnectionId) { clientConnectionId = default; return connection is not null && ByTypeHelpers.Get(connection.GetType()).TryGetClientConnectionId( connection, out clientConnectionId); } /// /// Clear all pools associated with the provided connection type /// public static bool TryClearAllPools(this DbConnection connection) => connection is not null && ByTypeHelpers.Get(connection.GetType()).TryClearAllPools(); /// /// Clear the pools associated with the provided connection /// public static bool TryClearPool(this DbConnection connection) => connection is not null && ByTypeHelpers.Get(connection.GetType()).TryClearPool(connection); private sealed class ByTypeHelpers { private static readonly ConcurrentDictionary s_byType = new ConcurrentDictionary(); private readonly Func? _getClientConnectionId; private readonly Action? _clearPool; private readonly Action? _clearAllPools; public bool TryGetClientConnectionId(DbConnection connection, out Guid clientConnectionId) { if (_getClientConnectionId is null) { clientConnectionId = default; return false; } clientConnectionId = _getClientConnectionId(connection); return true; } public bool TryClearPool(DbConnection connection) { if (_clearPool is null) return false; _clearPool(connection); return true; } public bool TryClearAllPools() { if (_clearAllPools is null) return false; _clearAllPools(); return true; } public static ByTypeHelpers Get(Type type) { if (!s_byType.TryGetValue(type, out var value)) { s_byType[type] = value = new ByTypeHelpers(type); } return value; } private ByTypeHelpers(Type type) { _getClientConnectionId = TryGetInstanceProperty("ClientConnectionId", type); try { var clearAllPools = type.GetMethod("ClearAllPools", BindingFlags.Public | BindingFlags.Static, null, Type.EmptyTypes, null); if (clearAllPools is not null) { _clearAllPools = (Action)Delegate.CreateDelegate(typeof(Action), clearAllPools); } } catch { } try { var clearPool = type.GetMethod("ClearPool", BindingFlags.Public | BindingFlags.Static, null, new[] { type }, null); if (clearPool is not null) { var p = Expression.Parameter(typeof(DbConnection), "connection"); var body = Expression.Call(clearPool, Expression.Convert(p, type)); var lambda = Expression.Lambda>(body, p); _clearPool = lambda.Compile(); } } catch { } } private static Func? TryGetInstanceProperty(string name, Type type) { try { var prop = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); if (prop is null || !prop.CanRead) return null; if (prop.PropertyType != typeof(T)) return null; var p = Expression.Parameter(typeof(DbConnection), "connection"); var body = Expression.Property(Expression.Convert(p, type), prop); var lambda = Expression.Lambda>(body, p); return lambda.Compile(); } catch { return null; } } } } }