using System; using System.Collections.Concurrent; using System.Data.Common; using System.Linq.Expressions; using System.Reflection; #nullable enable namespace Dapper.ProviderTools { /// /// Helper utilties for working with database exceptions /// public static class DbExceptionExtensions { /// /// Indicates whether the provided exception has an integer Number property with the supplied value /// public static bool IsNumber(this DbException exception, int number) => exception != null && ByTypeHelpers.Get(exception.GetType()).IsNumber(exception, number); private sealed class ByTypeHelpers { private static readonly ConcurrentDictionary s_byType = new ConcurrentDictionary(); private readonly Func? _getNumber; public bool IsNumber(DbException exception, int number) => _getNumber != null && _getNumber(exception) == number; 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) { _getNumber = TryGetInstanceProperty("Number", type); } private static Func? TryGetInstanceProperty(string name, Type type) { try { var prop = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); if (prop == null || !prop.CanRead) return null; if (prop.PropertyType != typeof(T)) return null; var p = Expression.Parameter(typeof(DbException), "exception"); var body = Expression.Property(Expression.Convert(p, type), prop); var lambda = Expression.Lambda>(body, p); return lambda.Compile(); } catch { return null; } } } } }