From 2d47030a810ebdaf9c5ca0da1b12474b6cc11f6c Mon Sep 17 00:00:00 2001 From: Havagan Date: Fri, 31 Jul 2020 16:06:55 -0400 Subject: [PATCH 1/3] Refactored the OracleValueConverter to handle OracleParameter values and allow Oracle native NUMBER values to convert to Int32 and Int64. Removed .NET 4.5.1 framework support. Updated Dapper version to 2.0.35. --- .../Dapper.Oracle.StrongName.csproj | 6 +- src/Dapper.Oracle/Dapper.Oracle.csproj | 6 +- src/Dapper.Oracle/OracleValueConverter.cs | 101 +++++++----- .../OracleValueConverterTests.cs | 147 ++++++++++++++++-- .../Tests.Dapper.Oracle.csproj | 1 + 5 files changed, 201 insertions(+), 60 deletions(-) diff --git a/src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj b/src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj index 0771ca4..8dc569f 100644 --- a/src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj +++ b/src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj @@ -3,7 +3,7 @@ false - netstandard2.0;net452 + netstandard2.0 @@ -15,9 +15,9 @@ - + - v4.5.2 + v4.6.1 \ No newline at end of file diff --git a/src/Dapper.Oracle/Dapper.Oracle.csproj b/src/Dapper.Oracle/Dapper.Oracle.csproj index c8944b0..acc412f 100644 --- a/src/Dapper.Oracle/Dapper.Oracle.csproj +++ b/src/Dapper.Oracle/Dapper.Oracle.csproj @@ -3,7 +3,7 @@ false - netstandard2.0;net452 + netstandard2.0 @@ -15,9 +15,9 @@ - + - v4.5.2 + v4.6.1 \ No newline at end of file diff --git a/src/Dapper.Oracle/OracleValueConverter.cs b/src/Dapper.Oracle/OracleValueConverter.cs index c691695..006d6ca 100644 --- a/src/Dapper.Oracle/OracleValueConverter.cs +++ b/src/Dapper.Oracle/OracleValueConverter.cs @@ -1,73 +1,88 @@ using System; using System.Collections.Generic; +using System.ComponentModel; +using System.Data.Common; using System.Linq; +using System.Reflection.Emit; using System.Text.RegularExpressions; namespace Dapper.Oracle { internal static class OracleValueConverter { - private static string[] OracleStringTypes { get; } = { "Oracle.DataAccess.Types.OracleString", "Oracle.ManagedDataAccess.Types.OracleString" }; - - public static T Convert(object val) + /// + /// Convert the value to the provided generic type. + /// + /// + /// + /// + public static T Convert(object value) { - if (val == null) + value = GetValue(value); + + if (value == null || value == DBNull.Value) { return default(T); } - if (val == DBNull.Value) + // Convert the Oracle native data type to .NET data type. + // See: https://docs.oracle.com/en/database/oracle/oracle-database/19/clrnt/datatype-conversion.html#GUID-70A2F34D-AB7F-4E0C-89C9-452A45FF1CAC + if (value is IConvertible) { - if (default(T) != null) - { - throw new ApplicationException("Attempting to cast a DBNull to a non nullable type!"); - } - - return default(T); + var nullableType = Nullable.GetUnderlyingType(typeof(T)); + return (T)System.Convert.ChangeType(value, nullableType ?? typeof(T)); } - Type valueType = val.GetType(); - if (typeof(T) == typeof(string) && OracleStringTypes.Contains(valueType.FullName)) - { - // Need this, because... you know,Oracle... - val = val.ToString(); + return default(T); + } - if ((string)val == "null") // Seriously. W.T.F ???? - { - return default(T); - } + /// + /// Gets the underlying primitive value from a nested type instance. + /// + /// + /// + private static object GetValue(object value) + { + if (value == null || value == DBNull.Value) + { + return null; + } - return (T)val; + // Recursively handle nested database parameters. + // An OracleParameter can have a value that is an instance of an Oracle data structure (OracleBlob, OracleDecimal, etc.). + // This assumes we want the underlying primitive value of the parameter. + while (value is DbParameter parameter) + { + value = parameter.Value; } - // We need this, because, you know, Oracle. OracleDecimal,OracleFloat,OracleYaddiAddy,OracleYourUncle etc value types. - if (Regex.IsMatch(valueType.FullName, @"Oracle\.\w+\.Types\.Oracle\w+")) + Type valueType = value.GetType(); + + if (IsOracleDataStructure(valueType)) { - // Fix Oracle 11g (to not throw the exception "Invalid operation on null data" if a function returns NULL) - var isNullProperty = valueType.GetProperty("IsNull"); - if (isNullProperty != null && isNullProperty.CanRead) - { - var isNull = (bool)isNullProperty.GetValue(val); - if (isNull) - { - if (default(T) != null) - { - throw new ApplicationException("Attempting to cast a DBNull to a non nullable type!"); - } - return default(T); - } - // If not isNull, continue and get the Value - } + var isNull = (bool)valueType.GetProperty("IsNull", typeof(bool))?.GetValue(value); - var valueProperty = valueType.GetProperty("Value"); - if (valueProperty != null && valueProperty.CanRead) + if (isNull) { - object reflected = valueProperty.GetValue(val); - return (T)reflected; + return null; } + + value = valueType.GetProperty("Value")?.GetValue(value); } - return (T)System.Convert.ChangeType(val, typeof(T)); + return value; + } + + /// + /// Determine if the type is an Oracle data structure. + /// + /// + /// + private static bool IsOracleDataStructure(Type valueType) + { + // We need this, because, you know, Oracle. OracleDecimal,OracleFloat,OracleYaddiAddy,OracleYourUncle etc value types. + // See: https://docs.oracle.com/en/database/oracle/oracle-database/19/odpnt/intro003.html#GUID-425C9EBA-CFFC-47FE-B490-604251714ACA + return Regex.IsMatch(valueType.FullName, @"Oracle\.\w+\.Types\.Oracle\w+"); } } } diff --git a/src/Tests.Dapper.Oracle/OracleValueConverterTests.cs b/src/Tests.Dapper.Oracle/OracleValueConverterTests.cs index 1b39101..52113aa 100644 --- a/src/Tests.Dapper.Oracle/OracleValueConverterTests.cs +++ b/src/Tests.Dapper.Oracle/OracleValueConverterTests.cs @@ -47,32 +47,157 @@ public void GetNullAsStringReturnsNull() [Fact] public void GetNullAsDecimalReturns0() { - var result = OracleValueConverter.Convert(null); + var result = OracleValueConverter.Convert(null); result.Should().Be(0); } [Fact] - public void GetOracleDecimalReturnsDecimal() + public void GetDbNullAsDecimalReturns0() { - var result = OracleValueConverter.Convert(new OracleDecimal(100)); - result.Should().Be(100); + var result = OracleValueConverter.Convert(DBNull.Value); + result.Should().Be(0); } [Fact] - public void GetOracleDateTimeReturnsDateTime() + public void GetNullAsInt32Returns0() { - var result = OracleValueConverter.Convert(new OracleDate(DateTime.Today)); - result.Should().Be(DateTime.Today); + var result = OracleValueConverter.Convert(null); + result.Should().Be(0); + } + + [Fact] + public void GetDbNullAsInt32Returns0() + { + var result = OracleValueConverter.Convert(DBNull.Value); + result.Should().Be(0); + } + + [Fact] + public void GetNullAsInt64Returns0() + { + var result = OracleValueConverter.Convert(null); + result.Should().Be(0); + } + + [Fact] + public void GetDbNullAsInt64Returns0() + { + var result = OracleValueConverter.Convert(DBNull.Value); + result.Should().Be(0); + } + + [Fact] + public void GetNullAsNullableDecimalReturnsNull() + { + var result = OracleValueConverter.Convert(null); + result.Should().BeNull(); + } + + [Fact] + public void GetNullAsNullableInt32ReturnsNull() + { + var result = OracleValueConverter.Convert(null); + result.Should().BeNull(); + } + + [Fact] + public void GetNullAsNullableInt64ReturnsNull() + { + var result = OracleValueConverter.Convert(null); + result.Should().BeNull(); + } + + [Fact] + public void GetDbNullAsDecimalReturnsNull() + { + var result = OracleValueConverter.Convert(DBNull.Value); + result.Should().Be(null); + } + + [Fact] + public void GetDbNullAsInt32ReturnsNull() + { + var result = OracleValueConverter.Convert(DBNull.Value); + result.Should().Be(null); + } + + [Fact] + public void GetDbNullAsInt64ReturnsNull() + { + var result = OracleValueConverter.Convert(DBNull.Value); + result.Should().Be(null); + } + + + [Fact] + public void GetOracleDecimalReturnsDecimal() + { + decimal expected = 100; + + var result = OracleValueConverter.Convert(new OracleDecimal(expected)); + + result.Should().Be(expected); } [Fact] - public void GetOracleNumberReturnAsDecimal() + public void GetOracleDecimalReturnsNullableDecimal() { decimal expected = 100; - var result = OracleValueConverter.Convert(100); + + var result = OracleValueConverter.Convert(new OracleDecimal(expected)); + + result.Should().Be(expected); + } + + [Fact] + public void GetOracleDecimalReturnsInt32() + { + decimal input = 100; + var expected = Convert.ToInt32(input); + + var result = OracleValueConverter.Convert(new OracleDecimal(input)); + result.Should().Be(expected); } - } - + [Fact] + public void GetOracleDecimalReturnsNullableInt32() + { + decimal input = 100; + var expected = Convert.ToInt32(input); + + var result = OracleValueConverter.Convert(new OracleDecimal(input)); + + result.Should().Be(expected); + } + + [Fact] + public void GetOracleDecimalReturnsInt64() + { + decimal input = 100; + var expected = Convert.ToInt64(input); + + var result = OracleValueConverter.Convert(new OracleDecimal(input)); + + result.Should().Be(expected); + } + + [Fact] + public void GetOracleDecimalReturnsNullableInt64() + { + decimal input = 100; + var expected = Convert.ToInt64(input); + + var result = OracleValueConverter.Convert(new OracleDecimal(input)); + + result.Should().Be(expected); + } + + [Fact] + public void GetOracleDateTimeReturnsDateTime() + { + var result = OracleValueConverter.Convert(new OracleDate(DateTime.Today)); + result.Should().Be(DateTime.Today); + } + } } diff --git a/src/Tests.Dapper.Oracle/Tests.Dapper.Oracle.csproj b/src/Tests.Dapper.Oracle/Tests.Dapper.Oracle.csproj index 5942887..68ce5f5 100644 --- a/src/Tests.Dapper.Oracle/Tests.Dapper.Oracle.csproj +++ b/src/Tests.Dapper.Oracle/Tests.Dapper.Oracle.csproj @@ -9,6 +9,7 @@ + From 0ea2db2f1ca04ee53e2246d5d05a8791555356e7 Mon Sep 17 00:00:00 2001 From: Havagan Date: Fri, 31 Jul 2020 17:27:19 -0400 Subject: [PATCH 2/3] Refactored GetParameter to return the OracleParameterInfo instead of the AttachedParam. --- src/Dapper.Oracle/OracleDynamicParameters.cs | 4 ++-- .../OracleDynamicParameterTests.cs | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Dapper.Oracle/OracleDynamicParameters.cs b/src/Dapper.Oracle/OracleDynamicParameters.cs index f113246..d19cda0 100644 --- a/src/Dapper.Oracle/OracleDynamicParameters.cs +++ b/src/Dapper.Oracle/OracleDynamicParameters.cs @@ -157,7 +157,7 @@ public IEnumerable ParameterNames /// The value, note DBNull.Value is not returned, instead the value is returned as null public T Get(string name) { - var paramInfo = Parameters[Clean(name)]; + var paramInfo = GetParameter(name); var attachedParam = paramInfo.AttachedParam; object val = attachedParam == null ? paramInfo.Value : attachedParam.Value; if (val == DBNull.Value) @@ -174,7 +174,7 @@ public T Get(string name) public OracleParameterInfo GetParameter(string name) { - return OracleMethodHelper.GetParameterInfo(Parameters[Clean(name)].AttachedParam); + return Parameters[Clean(name)]; } void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity) diff --git a/src/Tests.Dapper.Oracle/OracleDynamicParameterTests.cs b/src/Tests.Dapper.Oracle/OracleDynamicParameterTests.cs index 7fde200..0b7caa3 100644 --- a/src/Tests.Dapper.Oracle/OracleDynamicParameterTests.cs +++ b/src/Tests.Dapper.Oracle/OracleDynamicParameterTests.cs @@ -160,5 +160,18 @@ public void GetParameterValue() testObject.Get("Foo").Should().Be("Bar"); } + + [Fact] + public void GetParameter() + { + testObject.Add("Foo", "Bar", OracleMappingType.Varchar2); + + var param = testObject.GetParameter("Foo"); + + param.Should().NotBeNull(); + param.Name.Should().Be("Foo"); + param.Value.Should().Be("Bar"); + param.DbType.Should().Be(OracleMappingType.Varchar2); + } } } From 6b5ef096abcee10eac605f012f388bfac513ba1c Mon Sep 17 00:00:00 2001 From: Havagan Date: Mon, 3 Aug 2020 10:23:28 -0400 Subject: [PATCH 3/3] Added net461 as a target framework for base and strong name projects. https://github.com/DIPSAS/Dapper.Oracle/pull/44#pullrequestreview-459800252 --- src/Dapper.Oracle.StrongName/Dapper.Oracle.StrongName.csproj | 4 ++-- src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj | 3 ++- src/Dapper.Oracle/Dapper.Oracle.csproj | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Dapper.Oracle.StrongName/Dapper.Oracle.StrongName.csproj b/src/Dapper.Oracle.StrongName/Dapper.Oracle.StrongName.csproj index 74f5fe5..3433e01 100644 --- a/src/Dapper.Oracle.StrongName/Dapper.Oracle.StrongName.csproj +++ b/src/Dapper.Oracle.StrongName/Dapper.Oracle.StrongName.csproj @@ -3,7 +3,7 @@ false - netstandard2.0;net452 + netstandard2.0;net461 true key.snk @@ -24,6 +24,6 @@ - v4.5.2 + v4.6.1 \ No newline at end of file diff --git a/src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj b/src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj index 8dc569f..29c5201 100644 --- a/src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj +++ b/src/Dapper.Oracle/Dapper.Oracle.StrongName.csproj @@ -3,7 +3,7 @@ false - netstandard2.0 + netstandard2.0;net461 @@ -11,6 +11,7 @@ + diff --git a/src/Dapper.Oracle/Dapper.Oracle.csproj b/src/Dapper.Oracle/Dapper.Oracle.csproj index acc412f..06c0807 100644 --- a/src/Dapper.Oracle/Dapper.Oracle.csproj +++ b/src/Dapper.Oracle/Dapper.Oracle.csproj @@ -3,7 +3,7 @@ false - netstandard2.0 + netstandard2.0;net461 @@ -11,6 +11,7 @@ +