Skip to content

Commit

Permalink
Attempt to access BindByName on the outbox throws an exception when u…
Browse files Browse the repository at this point in the history
…sed in combination with DevArt.Data.Oracle (#1407)

* Fallback to PassParametersByName when BindByName is not available to support DevArt.

* Bump vulnerable dependencies for tests

* Read boolean as int if necessary
  • Loading branch information
danielmarbach authored Mar 12, 2024
1 parent 59c2c1f commit fa1f468
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 9 deletions.
15 changes: 9 additions & 6 deletions src/SqlPersistence/Config/SqlDialect_Oracle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public abstract partial class SqlDialect
/// </summary>
public partial class Oracle : SqlDialect
{
volatile PropertyInfo bindByName;
volatile PropertyInfo parametersByNameProperty;

internal override void SetJsonParameterValue(DbParameter parameter, object value)
{
Expand Down Expand Up @@ -42,16 +42,19 @@ internal override CommandWrapper CreateCommand(DbConnection connection)
{
var command = connection.CreateCommand();

if (bindByName == null)
if (parametersByNameProperty == null)
{
var type = command.GetType();
bindByName = type.GetProperty("BindByName");
if (bindByName == null)
parametersByNameProperty = type.GetProperty("BindByName") ??
// someone might be using DevArt Oracle provider which uses PassParametersByName
type.GetProperty("PassParametersByName");
if (parametersByNameProperty == null)
{
throw new Exception($"Could not extract field 'BindByName' from '{type.AssemblyQualifiedName}'.");
throw new Exception($"Could not extract property 'BindByName' nor 'PassParametersByName' from '{type.AssemblyQualifiedName}'. The property is required to make sure the parameters passed to the commands can be passed by name and do not depend on the order they are added to the command.");
}
}
bindByName.SetValue(command, true);

parametersByNameProperty.SetValue(command, true);

return new CommandWrapper(command, this);
}
Expand Down
9 changes: 7 additions & 2 deletions src/SqlPersistence/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,21 @@ public static void AddParameter(this DbCommand command, string name, Version val
public static async Task<bool> GetBoolAsync(this DbDataReader reader, int position, CancellationToken cancellationToken = default)
{
var type = reader.GetFieldType(position);
// MySql stores bools as ints
// MySql stores bools as longs (ulong)
if (type == typeof(ulong))
{
return Convert.ToBoolean(await reader.GetFieldValueAsync<ulong>(position, cancellationToken).ConfigureAwait(false));
}
// In Oracle we store bools as NUMBER(1,0) (short).
// In Oracle default driver store bools as NUMBER(1,0) (short).
if (type == typeof(short))
{
return Convert.ToBoolean(await reader.GetFieldValueAsync<short>(position, cancellationToken).ConfigureAwait(false));
}
// or it might be stored as ints.
if (type == typeof(int))
{
return Convert.ToBoolean(await reader.GetFieldValueAsync<int>(position, cancellationToken).ConfigureAwait(false));
}
return await reader.GetFieldValueAsync<bool>(position, cancellationToken).ConfigureAwait(false);
}

Expand Down
2 changes: 1 addition & 1 deletion src/TestHelper/TestHelper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageReference Include="MySql.Data" Version="8.0.29" ExcludeAssets="contentFiles" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.3" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" />
</ItemGroup>

<!-- Workaround to prevent VS test discovery error -->
Expand Down

0 comments on commit fa1f468

Please sign in to comment.