Skip to content
Open
2 changes: 1 addition & 1 deletion build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ if ($connectionstring)
dotnet restore .\src\Dapper.Oracle.sln
dotnet build .\src\Dapper.Oracle.sln
dotnet test .\src\Dapper.Oracle.sln
dotnet pack .\src\Dapper.Oracle.sln -p:PackageVersion=2.0.4
dotnet pack .\src\Dapper.Oracle.sln -p:PackageVersion=2.1.0
10 changes: 5 additions & 5 deletions src/Dapper.Oracle.StrongName/Dapper.Oracle.StrongName.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\common.targets" />
<PropertyGroup>
<IsTestProject>false</IsTestProject>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net10.0</TargetFrameworks>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
Expand All @@ -13,10 +13,10 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Dapper.StrongName" Version="2.0.123" />
<PackageReference Include="Dapper.StrongName" Version="2.1.72" />
</ItemGroup>

<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.300" />
</ItemGroup>
</Project>
</Project>
22 changes: 12 additions & 10 deletions src/Dapper.Oracle/Dapper.Oracle.csproj
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\common.targets" />
<PropertyGroup>
<IsTestProject>false</IsTestProject>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
<PackageReleaseNotes>
Version 2.0.4:
- Updated to use .net 7.0.
- Dependency switched to most recent version of Dapper: 2.0.123.
- Dependencies updated also for test project.
</PackageReleaseNotes>
Version 2.1.0:
- Updated to use .net 10.0.
- Dependency switched to most recent version of Dapper: 2.1.72.
- Dependencies updated also for test project.
- Fix OracleDecimal to System.Decimal conversion overflow handling
- Enriching OracleParameterInfo with flag for PII data
</PackageReleaseNotes>
<!--<PackageReadmeFile>..\..\README.MD</PackageReadmeFile>-->
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.123" />
<PackageReference Include="Dapper" Version="2.1.72" />
</ItemGroup>

<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.300" />
</ItemGroup>
</Project>
</Project>
17 changes: 17 additions & 0 deletions src/Dapper.Oracle/OracleDecimalConversion.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Dapper.Oracle
{
/// <summary>
/// Maps Oracle native numeric values (especially <c>OracleDecimal</c>) to <see cref="decimal"/>,
/// using <c>OracleDecimal.SetPrecision(..., 28)</c> before reading <c>System.Decimal</c> (same idea as legacy ADO.NET).
/// </summary>
/// <remarks>
/// Used internally by <see cref="OracleValueConverter"/> (for example from <see cref="OracleDynamicParameters.Get{T}"/>).
/// Dapper's default <c>Query&lt;decimal&gt;</c> / <c>ExecuteScalar&lt;decimal&gt;</c> does not call this; for result sets use this helper on raw values, SQL <c>CAST</c>, or a custom <c>TypeHandler</c>.
/// </remarks>
public static class OracleDecimalConversion
{
public static decimal ToDecimal(object value) => OracleValueConverter.Convert<decimal>(value);

public static decimal? ToDecimalNullable(object value) => OracleValueConverter.Convert<decimal?>(value);
}
}
27 changes: 16 additions & 11 deletions src/Dapper.Oracle/OracleDynamicParameters.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//// Based on Gist found here: https://gist.github.com/vijaysg/3096151
//// Based on Gist found here: https://gist.github.com/vijaysg/3096151

using System;
using System.Collections;
Expand Down Expand Up @@ -109,6 +109,7 @@ public void AddDynamicParams(dynamic param)
/// <param name="sourceColumn"></param>
/// <param name="sourceVersion"></param>
/// <param name="collectionType"></param>
/// <param name="maskValueWhenLogging">a flag that this param contains sensitive data and it must be masked in case of logging values</param>
public void Add(
string name,
object value = null,
Expand All @@ -121,7 +122,8 @@ public void Add(
string sourceColumn = null,
DataRowVersion? sourceVersion = null,
OracleMappingCollectionType? collectionType = null,
int[] arrayBindSize = null)
int[] arrayBindSize = null,
bool maskValueWhenLogging = false)
{
Parameters[Clean(name)] = new OracleParameterInfo()
{
Expand All @@ -136,7 +138,8 @@ public void Add(
SourceColumn = sourceColumn,
SourceVersion = sourceVersion ?? DataRowVersion.Current,
CollectionType = collectionType ?? OracleMappingCollectionType.None,
ArrayBindSize = arrayBindSize
ArrayBindSize = arrayBindSize,
MaskValueWhenLogging = maskValueWhenLogging
};
}

Expand Down Expand Up @@ -167,7 +170,7 @@ public T Get<T>(string name)
}
return default(T);
}

return OracleValueConverter.Convert<T>(val);
}

Expand Down Expand Up @@ -239,10 +242,10 @@ protected virtual void AddParameters(IDbCommand command, SqlMapper.Identity iden
}

OracleMethodHelper.SetOracleParameters(p, param);

p.Direction = param.ParameterDirection;
var val = param.Value;

var val = param.Value;

if (val != null && OracleTypeMapper.HasTypeHandler(val.GetType(), out var handler))
{
Expand All @@ -251,7 +254,7 @@ protected virtual void AddParameters(IDbCommand command, SqlMapper.Identity iden
else
{
p.Value = val ?? DBNull.Value;

var s = val as string;
if (s?.Length <= 4000)
{
Expand All @@ -262,8 +265,8 @@ protected virtual void AddParameters(IDbCommand command, SqlMapper.Identity iden
{
p.Size = param.Size.Value;
}
}
}

if (add)
{
command.Parameters.Add(p);
Expand Down Expand Up @@ -320,6 +323,8 @@ public class OracleParameterInfo
public OracleParameterMappingStatus Status { get; set; }

public IDbDataParameter AttachedParam { get; set; }

public bool MaskValueWhenLogging { get; set; }
}

/// <summary>
Expand All @@ -328,4 +333,4 @@ public class OracleParameterInfo
/// <returns></returns>
public IEnumerator GetEnumerator() => Parameters.GetEnumerator();
}
}
}
54 changes: 52 additions & 2 deletions src/Dapper.Oracle/OracleValueConverter.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text.RegularExpressions;

Expand Down Expand Up @@ -126,7 +127,10 @@ private static T ConvertArray<T>(object value)
var decimalArray = new decimal[arr.Length];
for (int i = 0; i < arr.Length; i++)
{
decimalArray[i] = decimal.Parse(arr.GetValue(i)?.ToString());
var raw = arr.GetValue(i);
decimalArray[i] = TryReduceOracleDecimalToSystemDecimal(raw, out var systemDecimal)
? systemDecimal
: decimal.Parse(raw?.ToString() ?? "0");
}
return (T)System.Convert.ChangeType(decimalArray, nullableType ?? typeof(T));
case "System.Boolean[]":
Expand Down Expand Up @@ -187,6 +191,12 @@ private static object GetValue(object value)
return null;
}

// Legacy ADO.NET: OracleDecimal.SetPrecision(..., 28).Value — no compile-time reference to Oracle.ManagedDataAccess.
if (TryReduceOracleDecimalToSystemDecimal(value, out var asDecimal))
{
return asDecimal;
}

var val = valueType.GetProperty("Value")?.GetValue(value);
if (val != null)
{
Expand All @@ -208,5 +218,45 @@ private static bool IsOracleDataStructure(Type valueType)
// 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+");
}

private const string OracleDecimalTypeFullName = "Oracle.ManagedDataAccess.Types.OracleDecimal";

private static bool IsOracleDecimal(Type type) =>
type != null && type.FullName == OracleDecimalTypeFullName;

/// <summary>
/// Calls <c>OracleDecimal.SetPrecision(instance, 28).Value</c> via reflection when ODP.NET is loaded at runtime.
/// </summary>
private static bool TryReduceOracleDecimalToSystemDecimal(object value, out decimal systemDecimal)
{
systemDecimal = default;
if (value == null || !IsOracleDecimal(value.GetType()))
{
return false;
}

var t = value.GetType();
var setPrecision = t.GetMethod(
"SetPrecision",
BindingFlags.Public | BindingFlags.Static,
binder: null,
types: new[] { t, typeof(int) },
modifiers: null);
if (setPrecision == null)
{
return false;
}

var reduced = setPrecision.Invoke(null, new object[] { value, 28 });
var valueProp = t.GetProperty("Value");
var boxed = valueProp?.GetValue(reduced);
if (boxed is decimal d)
{
systemDecimal = d;
return true;
}

return false;
}
}
}
5 changes: 2 additions & 3 deletions src/Tests.Dapper.Oracle/IntegrationTests/DatabaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ public void Dispose()

public async Task InitializeAsync()
{
// Prefer DA_OR_CONNECTION (CI / local Oracle). Do not overwrite it — a hardcoded default used to break env-based runs.
var connectionString = Environment.GetEnvironmentVariable("DA_OR_CONNECTION");

connectionString = "Data Source=localhost/dips;User Id=system;Password=oracle";

if (string.IsNullOrEmpty(connectionString))
{
var si = new ProcessStartInfo("powershell", @".\LocalOracleDockerDb.ps1");
Expand Down Expand Up @@ -100,4 +99,4 @@ private static string GetBootstrapFolder()

}
}
}
}
18 changes: 17 additions & 1 deletion src/Tests.Dapper.Oracle/OracleValueConverterTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Text;
using Dapper.Oracle;
using FluentAssertions;
Expand Down Expand Up @@ -249,6 +249,22 @@ public void GetOracleDecimalReturnsDecimal()
result.Should().Be(expected);
}

[Fact]
public void GetOracleDecimalMatchesExplicitSetPrecision28()
{
foreach (var d in new[]
{
0m,
1m,
0.4690679611650485436893203883m,
})
{
var od = new OracleDecimal(d);
var expected = OracleDecimal.SetPrecision(od, 28).Value;
OracleValueConverter.Convert<decimal>(od).Should().Be(expected);
}
}

[Fact]
public void GetOracleDecimalReturnsNullableDecimal()
{
Expand Down
25 changes: 15 additions & 10 deletions src/Tests.Dapper.Oracle/Tests.Dapper.Oracle.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\common.targets" />
<PropertyGroup>
<IsTestProject>true</IsTestProject>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
<!-- Integration tests need a live Oracle (see DatabaseFixture). Default: skip them; run all with: dotnet test -p:RunIntegrationTests=true -->
<RunIntegrationTests Condition="'$(RunIntegrationTests)' == ''">false</RunIntegrationTests>
</PropertyGroup>
<PropertyGroup Condition="'$(RunIntegrationTests)' != 'true'">
<VSTestTestCaseFilter>Category!=Integration&amp;Category!=IntegrationTest</VSTestTestCaseFilter>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="3.21.100" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.console" Version="2.4.2">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.26.200" />
<PackageReference Include="System.Data.SqlClient" Version="4.9.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.console" Version="2.9.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand All @@ -36,7 +41,7 @@
<EmbeddedResource Include="IntegrationTests\Schema.sql" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.300" />
</ItemGroup>

</Project>
</Project>