From 8bb959319ac3c3c06390d1ffb2ec37e2d50589f9 Mon Sep 17 00:00:00 2001 From: Daniel Marbach Date: Wed, 13 Mar 2024 07:45:05 +0000 Subject: [PATCH] Attempt to access BindByName on the outbox throws an exception when used in combination with DevArt.Data.Oracle (#1419) * Fallback to PassParametersByName when BindByName is not available to support DevArt. * Read boolean as int if necessary --- .github/workflows/ci.yml | 4 ++-- src/SqlPersistence/Config/SqlDialect_Oracle.cs | 15 +++++++++------ src/SqlPersistence/Extensions.cs | 9 +++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2856034d9..026926772 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: creds: ${{ secrets.AZURE_ACI_CREDENTIALS }} - name: Setup SQL Server if: matrix.engine == 'SqlServer' - uses: Particular/install-sql-server-action@v1.0.0 + uses: Particular/install-sql-server-action@v1.1.0 with: connection-string-env-var: SQLServerConnectionString catalog: nservicebus @@ -150,7 +150,7 @@ jobs: - name: Setup Oracle id: setup-oracle if: matrix.engine == 'Oracle' - uses: Particular/setup-oracle-action@v1.3.0 + uses: Particular/setup-oracle-action@v1.4.0 with: connection-string-name: OracleConnectionString init-script: ./.github/workflows/scripts/setup-database-users-and-permissions.sql diff --git a/src/SqlPersistence/Config/SqlDialect_Oracle.cs b/src/SqlPersistence/Config/SqlDialect_Oracle.cs index 4eed1145a..8bd9175e6 100644 --- a/src/SqlPersistence/Config/SqlDialect_Oracle.cs +++ b/src/SqlPersistence/Config/SqlDialect_Oracle.cs @@ -13,7 +13,7 @@ public abstract partial class SqlDialect /// public partial class Oracle : SqlDialect { - volatile PropertyInfo bindByName; + volatile PropertyInfo parametersByNameProperty; internal override void SetJsonParameterValue(DbParameter parameter, object value) { @@ -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); } diff --git a/src/SqlPersistence/Extensions.cs b/src/SqlPersistence/Extensions.cs index 8475a7f26..61cc6deff 100644 --- a/src/SqlPersistence/Extensions.cs +++ b/src/SqlPersistence/Extensions.cs @@ -61,16 +61,21 @@ public static void AddParameter(this DbCommand command, string name, Version val public static async Task GetBoolAsync(this DbDataReader reader, int position) { 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(position).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(position).ConfigureAwait(false)); } + // or it might be stored as ints. + if (type == typeof(int)) + { + return Convert.ToBoolean(await reader.GetFieldValueAsync(position).ConfigureAwait(false)); + } return await reader.GetFieldValueAsync(position).ConfigureAwait(false); }