diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3530/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3530/Fixture.cs
new file mode 100644
index 00000000000..8bfbaddbf4e
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3530/Fixture.cs
@@ -0,0 +1,230 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Data;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.ExceptionServices;
+using System.Text;
+using NHibernate.SqlTypes;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH3530;
+using System.Threading.Tasks;
+using System.Threading;
+
+[TestFixture]
+public class FixtureAsync : BugTestCase
+{
+ private CultureInfo initialCulture;
+
+ [OneTimeSetUp]
+ public void FixtureSetup()
+ {
+ initialCulture = CurrentCulture;
+ }
+
+ [OneTimeTearDown]
+ public void FixtureTearDown()
+ {
+ CurrentCulture = initialCulture;
+ }
+
+ protected override void OnTearDown()
+ {
+ using var session = OpenSession();
+ using var transaction = session.BeginTransaction();
+
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+
+ protected override void CreateSchema()
+ {
+ CreateTable("Integer");
+ CreateTable("DateTime");
+ CreateTable("Double");
+ CreateTable("Decimal");
+ CreateTable("Float");
+
+ base.CreateSchema();
+ }
+
+ ///
+ /// This function creates the schema for our custom entities.
+ /// If the SchemaExporter provided a mechanism to override the database
+ /// type, this method would not be required.
+ ///
+ ///
+ private void CreateTable(string name)
+ {
+ var sb = new StringBuilder();
+ var guidType = Dialect.GetTypeName(SqlTypeFactory.Guid);
+ var stringType = Dialect.GetTypeName(SqlTypeFactory.GetAnsiString(255));
+
+ var catalog = GetQuotedDefaultCatalog();
+ var schema = GetQuotedDefaultSchema();
+ var table = GetQualifiedName(catalog, schema, $"{name}Entity");
+
+ sb.Append($"{Dialect.CreateTableString} {table} (");
+
+ // Generate columns
+ sb.Append($"Id {guidType}, ");
+ sb.Append($"DataValue {stringType}");
+
+ // Add the primary key contraint for the identity column
+ sb.Append($", {Dialect.PrimaryKeyString} ( Id )");
+ sb.Append(')');
+
+ using var cn = Sfi.ConnectionProvider.GetConnection();
+ try
+ {
+ using var cmd = cn.CreateCommand();
+
+ cmd.CommandText = sb.ToString();
+ cmd.ExecuteNonQuery();
+ }
+ catch (Exception ex)
+ {
+ Assert.Warn($"Creating the schema failed, assuming it already exists. {ex}");
+ }
+ finally
+ {
+ Sfi.ConnectionProvider.CloseConnection(cn);
+ }
+ }
+
+ private string GetQuotedDefaultCatalog()
+ {
+ var t = cfg.GetType();
+ var getQuotedDefaultCatalog = t.GetMethod("GetQuotedDefaultCatalog", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ return (string) getQuotedDefaultCatalog.Invoke(cfg, [Dialect]);
+ }
+
+ private string GetQuotedDefaultSchema()
+ {
+ var t = cfg.GetType();
+ var getQuotedDefaultSchema = t.GetMethod("GetQuotedDefaultSchema", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ return (string) getQuotedDefaultSchema.Invoke(cfg, [Dialect]);
+ }
+
+ private string GetQualifiedName(string catalog, string schema, string name)
+ {
+ return Dialect.Qualify(catalog, schema, name);
+ }
+
+ private async Task PerformTestAsync(CultureInfo from, CultureInfo to, T expectedValue, Action assert, CancellationToken cancellationToken = default(CancellationToken))
+ where T : struct
+ where U : DataEntity, new()
+ {
+ object id;
+
+ CurrentCulture = from;
+ using (var session = OpenSession())
+ using (var tx = session.BeginTransaction())
+ {
+ var entity = new U()
+ {
+ DataValue = expectedValue
+ };
+
+ id = await (session.SaveAsync(entity, cancellationToken));
+ await (tx.CommitAsync(cancellationToken));
+ }
+
+ CurrentCulture = to;
+ using (var session = OpenSession())
+ using (var tx = session.BeginTransaction())
+ {
+ AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
+ var entity = await (session.GetAsync(id, cancellationToken));
+ AppDomain.CurrentDomain.FirstChanceException -= OnFirstChanceException;
+
+ assert(expectedValue, entity.DataValue);
+ }
+ }
+
+ private void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs e)
+ {
+ Assert.Warn($"Driver threw a {e.Exception.GetType().Name} exception while retrieving the value.");
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public async Task TestNHDateTimeAsync(CultureInfo from, CultureInfo to)
+ {
+ var leapDay = new DateTime(2024, 2, 29, new GregorianCalendar(GregorianCalendarTypes.USEnglish));
+
+ await (PerformTestAsync(from, to, leapDay, (expected, actual) => Assert.AreEqual(expected, actual)));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public async Task TestDateTimeAsync(CultureInfo from, CultureInfo to)
+ {
+ var leapDay = new DateTime(2024, 2, 29, new GregorianCalendar(GregorianCalendarTypes.USEnglish));
+
+ await (PerformTestAsync(from, to, leapDay, (expected, actual) => Assert.AreEqual(expected, actual)));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public async Task TestDecimalAsync(CultureInfo from, CultureInfo to)
+ {
+ decimal decimalValue = 12.3m;
+
+ await (PerformTestAsync(from, to, decimalValue, (expected, actual) => Assert.AreEqual(expected, actual)));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public async Task TestDoubleAsync(CultureInfo from, CultureInfo to)
+ {
+ double doubleValue = 12.3d;
+
+ await (PerformTestAsync(from, to, doubleValue,
+ (expected, actual) => Assert.True(Math.Abs(expected - actual) < double.Epsilon, $"Expected {expected} but was {actual}\n")
+ ));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+
+ public async Task TestIntegerAsync(CultureInfo from, CultureInfo to)
+ {
+ int integerValue = 123;
+
+ await (PerformTestAsync(from, to, integerValue, (expected, actual) => Assert.AreEqual(expected, actual)));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public async Task TestFloatAsync(CultureInfo from, CultureInfo to)
+ {
+ float floatValue = 12.3f;
+
+ await (PerformTestAsync(from, to, floatValue,
+ (expected, actual) => Assert.True(Math.Abs(expected - actual) < float.Epsilon, $"Expected {expected} but was {actual}\n")
+ ));
+ }
+
+ private CultureInfo CurrentCulture
+ {
+ get => CultureInfo.CurrentCulture;
+ set => CultureInfo.CurrentCulture = value;
+ }
+
+ public static object[][] GetTestCases()
+ {
+ return [
+ [new CultureInfo("en-US"), new CultureInfo("de-DE")],
+ [new CultureInfo("en-US"), new CultureInfo("ar-SA", false)],
+ [new CultureInfo("en-US"), new CultureInfo("th-TH", false)],
+ ];
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3530/Entities.cs b/src/NHibernate.Test/NHSpecificTest/GH3530/Entities.cs
new file mode 100644
index 00000000000..c521c48831a
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3530/Entities.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.GH3530;
+
+public abstract class Entity
+{
+ public virtual Guid Id { get; set; }
+}
+
+public abstract class DataEntity:Entity where T : struct
+{
+ public virtual T DataValue { get; set; }
+}
+
+public class IntegerEntity : DataEntity { }
+public class DateTimeEntity : DataEntity { }
+
+public class DoubleEntity : DataEntity { }
+public class DecimalEntity : DataEntity { }
+public class FloatEntity : DataEntity { }
+public class NHDateTimeEntity : DataEntity { }
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3530/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3530/Fixture.cs
new file mode 100644
index 00000000000..f76e831e93f
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3530/Fixture.cs
@@ -0,0 +1,218 @@
+using System;
+using System.Data;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.ExceptionServices;
+using System.Text;
+using NHibernate.SqlTypes;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.GH3530;
+
+[TestFixture]
+public class Fixture : BugTestCase
+{
+ private CultureInfo initialCulture;
+
+ [OneTimeSetUp]
+ public void FixtureSetup()
+ {
+ initialCulture = CurrentCulture;
+ }
+
+ [OneTimeTearDown]
+ public void FixtureTearDown()
+ {
+ CurrentCulture = initialCulture;
+ }
+
+ protected override void OnTearDown()
+ {
+ using var session = OpenSession();
+ using var transaction = session.BeginTransaction();
+
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+
+ protected override void CreateSchema()
+ {
+ CreateTable("Integer");
+ CreateTable("DateTime");
+ CreateTable("Double");
+ CreateTable("Decimal");
+ CreateTable("Float");
+
+ base.CreateSchema();
+ }
+
+ ///
+ /// This function creates the schema for our custom entities.
+ /// If the SchemaExporter provided a mechanism to override the database
+ /// type, this method would not be required.
+ ///
+ ///
+ private void CreateTable(string name)
+ {
+ var sb = new StringBuilder();
+ var guidType = Dialect.GetTypeName(SqlTypeFactory.Guid);
+ var stringType = Dialect.GetTypeName(SqlTypeFactory.GetAnsiString(255));
+
+ var catalog = GetQuotedDefaultCatalog();
+ var schema = GetQuotedDefaultSchema();
+ var table = GetQualifiedName(catalog, schema, $"{name}Entity");
+
+ sb.Append($"{Dialect.CreateTableString} {table} (");
+
+ // Generate columns
+ sb.Append($"Id {guidType}, ");
+ sb.Append($"DataValue {stringType}");
+
+ // Add the primary key contraint for the identity column
+ sb.Append($", {Dialect.PrimaryKeyString} ( Id )");
+ sb.Append(')');
+
+ using var cn = Sfi.ConnectionProvider.GetConnection();
+ try
+ {
+ using var cmd = cn.CreateCommand();
+
+ cmd.CommandText = sb.ToString();
+ cmd.ExecuteNonQuery();
+ }
+ catch (Exception ex)
+ {
+ Assert.Warn($"Creating the schema failed, assuming it already exists. {ex}");
+ }
+ finally
+ {
+ Sfi.ConnectionProvider.CloseConnection(cn);
+ }
+ }
+
+ private string GetQuotedDefaultCatalog()
+ {
+ var t = cfg.GetType();
+ var getQuotedDefaultCatalog = t.GetMethod("GetQuotedDefaultCatalog", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ return (string) getQuotedDefaultCatalog.Invoke(cfg, [Dialect]);
+ }
+
+ private string GetQuotedDefaultSchema()
+ {
+ var t = cfg.GetType();
+ var getQuotedDefaultSchema = t.GetMethod("GetQuotedDefaultSchema", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ return (string) getQuotedDefaultSchema.Invoke(cfg, [Dialect]);
+ }
+
+ private string GetQualifiedName(string catalog, string schema, string name)
+ {
+ return Dialect.Qualify(catalog, schema, name);
+ }
+
+ private void PerformTest(CultureInfo from, CultureInfo to, T expectedValue, Action assert)
+ where T : struct
+ where U : DataEntity, new()
+ {
+ object id;
+
+ CurrentCulture = from;
+ using (var session = OpenSession())
+ using (var tx = session.BeginTransaction())
+ {
+ var entity = new U()
+ {
+ DataValue = expectedValue
+ };
+
+ id = session.Save(entity);
+ tx.Commit();
+ }
+
+ CurrentCulture = to;
+ using (var session = OpenSession())
+ using (var tx = session.BeginTransaction())
+ {
+ AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;
+ var entity = session.Get(id);
+ AppDomain.CurrentDomain.FirstChanceException -= OnFirstChanceException;
+
+ assert(expectedValue, entity.DataValue);
+ }
+ }
+
+ private void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs e)
+ {
+ Assert.Warn($"Driver threw a {e.Exception.GetType().Name} exception while retrieving the value.");
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public void TestNHDateTime(CultureInfo from, CultureInfo to)
+ {
+ var leapDay = new DateTime(2024, 2, 29, new GregorianCalendar(GregorianCalendarTypes.USEnglish));
+
+ PerformTest(from, to, leapDay, (expected, actual) => Assert.AreEqual(expected, actual));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public void TestDateTime(CultureInfo from, CultureInfo to)
+ {
+ var leapDay = new DateTime(2024, 2, 29, new GregorianCalendar(GregorianCalendarTypes.USEnglish));
+
+ PerformTest(from, to, leapDay, (expected, actual) => Assert.AreEqual(expected, actual));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public void TestDecimal(CultureInfo from, CultureInfo to)
+ {
+ decimal decimalValue = 12.3m;
+
+ PerformTest(from, to, decimalValue, (expected, actual) => Assert.AreEqual(expected, actual));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public void TestDouble(CultureInfo from, CultureInfo to)
+ {
+ double doubleValue = 12.3d;
+
+ PerformTest(from, to, doubleValue,
+ (expected, actual) => Assert.True(Math.Abs(expected - actual) < double.Epsilon, $"Expected {expected} but was {actual}\n")
+ );
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+
+ public void TestInteger(CultureInfo from, CultureInfo to)
+ {
+ int integerValue = 123;
+
+ PerformTest(from, to, integerValue, (expected, actual) => Assert.AreEqual(expected, actual));
+ }
+
+ [Test, TestCaseSource(nameof(GetTestCases))]
+ public void TestFloat(CultureInfo from, CultureInfo to)
+ {
+ float floatValue = 12.3f;
+
+ PerformTest(from, to, floatValue,
+ (expected, actual) => Assert.True(Math.Abs(expected - actual) < float.Epsilon, $"Expected {expected} but was {actual}\n")
+ );
+ }
+
+ private CultureInfo CurrentCulture
+ {
+ get => CultureInfo.CurrentCulture;
+ set => CultureInfo.CurrentCulture = value;
+ }
+
+ public static object[][] GetTestCases()
+ {
+ return [
+ [new CultureInfo("en-US"), new CultureInfo("de-DE")],
+ [new CultureInfo("en-US"), new CultureInfo("ar-SA", false)],
+ [new CultureInfo("en-US"), new CultureInfo("th-TH", false)],
+ ];
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/GH3530/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH3530/Mappings.hbm.xml
new file mode 100644
index 00000000000..2053587ed5c
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/GH3530/Mappings.hbm.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate.Test/TypesTest/BooleanTypeFixture.cs b/src/NHibernate.Test/TypesTest/BooleanTypeFixture.cs
index 2c394a42472..1106e7f5256 100644
--- a/src/NHibernate.Test/TypesTest/BooleanTypeFixture.cs
+++ b/src/NHibernate.Test/TypesTest/BooleanTypeFixture.cs
@@ -59,6 +59,9 @@ public void GetByIndex(bool expected)
BooleanType type = NHibernateUtil.Boolean;
var session = Substitute.For();
var reader = Substitute.For();
+
+ reader.GetBoolean(index0).Returns(expected);
+ reader.GetBoolean(index1).Returns(expected);
reader[index0].Returns(expected);
reader[index1].Returns(expected);
@@ -78,8 +81,11 @@ public void GetByName(bool expected)
var type = NHibernateUtil.Boolean;
var session = Substitute.For();
var reader = Substitute.For();
+
reader.GetOrdinal(name0).Returns(0);
reader.GetOrdinal(name1).Returns(1);
+ reader.GetBoolean(0).Returns(expected);
+ reader.GetBoolean(1).Returns(expected);
reader[0].Returns(expected);
reader[1].Returns(expected);
diff --git a/src/NHibernate.Test/TypesTest/CharBooleanTypeFixture.cs b/src/NHibernate.Test/TypesTest/CharBooleanTypeFixture.cs
index d07c2856aaa..ccfacb9e2a7 100644
--- a/src/NHibernate.Test/TypesTest/CharBooleanTypeFixture.cs
+++ b/src/NHibernate.Test/TypesTest/CharBooleanTypeFixture.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Data.Common;
using NHibernate.Engine;
using NHibernate.SqlTypes;
@@ -19,6 +19,9 @@ public void GetByIndex(bool expected)
CharBooleanType type =new CharBooleanTypeStub();
var session = Substitute.For();
var reader = Substitute.For();
+
+ reader.GetString(index0).Returns(expected.ToString());
+ reader.GetString(index1).Returns(expected.ToString());
reader[index0].Returns(expected.ToString());
reader[index1].Returns(expected.ToString());
diff --git a/src/NHibernate/AdoNet/AbstractBatcher.cs b/src/NHibernate/AdoNet/AbstractBatcher.cs
index 99522150a2a..427bb671ab5 100644
--- a/src/NHibernate/AdoNet/AbstractBatcher.cs
+++ b/src/NHibernate/AdoNet/AbstractBatcher.cs
@@ -252,7 +252,7 @@ private DbDataReader DoExecuteReader(DbCommand cmd)
{
try
{
- var reader = cmd.ExecuteReader();
+ var reader = Driver.ExecuteReader(cmd);
if (reader == null)
{
// MySql may return null instead of an exception, by example when the query is canceled by another thread.
diff --git a/src/NHibernate/AdoNet/DbDataReaderExtensions.cs b/src/NHibernate/AdoNet/DbDataReaderExtensions.cs
new file mode 100644
index 00000000000..1634bc7cfcc
--- /dev/null
+++ b/src/NHibernate/AdoNet/DbDataReaderExtensions.cs
@@ -0,0 +1,279 @@
+using System;
+using System.Data.Common;
+
+namespace NHibernate.AdoNet
+{
+ internal static class DbDataReaderExtensions
+ {
+ public static bool TryGetBoolean(this DbDataReader rs, int ordinal, out bool value)
+ {
+ try
+ {
+ value = rs.GetBoolean(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetByte(this DbDataReader rs, int ordinal, out byte value)
+ {
+ try
+ {
+ value = rs.GetByte(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetChar(this DbDataReader rs, int ordinal, out char value)
+ {
+ try
+ {
+ value = rs.GetChar(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetDecimal(this DbDataReader rs, int ordinal, out decimal value)
+ {
+ try
+ {
+ value = rs.GetDecimal(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetDouble(this DbDataReader rs, int ordinal, out double value)
+ {
+ try
+ {
+ value = rs.GetDouble(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetDateTime(this DbDataReader rs, int ordinal, out DateTime value)
+ {
+ try
+ {
+ value = rs.GetDateTime(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+ public static bool TryGetFloat(this DbDataReader rs, int ordinal, out float value)
+ {
+ try
+ {
+ value = rs.GetFloat(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+ public static bool TryGetGuid(this DbDataReader rs, int ordinal, out Guid value)
+ {
+ try
+ {
+ value = rs.GetGuid(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetUInt16(this DbDataReader rs, int ordinal, out ushort value)
+ {
+ var dbValue = rs[ordinal];
+
+ if (dbValue is ushort)
+ {
+ value = (ushort) dbValue;
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ public static bool TryGetInt16(this DbDataReader rs, int ordinal, out short value)
+ {
+ try
+ {
+ value = rs.GetInt16(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+ public static bool TryGetInt32(this DbDataReader rs, int ordinal, out int value)
+ {
+ try
+ {
+ value = rs.GetInt32(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetUInt32(this DbDataReader rs, int ordinal, out uint value)
+ {
+ var dbValue = rs[ordinal];
+
+ if (dbValue is uint)
+ {
+ value = (uint) dbValue;
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ public static bool TryGetInt64(this DbDataReader rs, int ordinal, out long value)
+ {
+ try
+ {
+ value = rs.GetInt64(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetUInt64(this DbDataReader rs, int ordinal, out ulong value)
+ {
+ var dbValue = rs[ordinal];
+
+ if (dbValue is ulong)
+ {
+ value = (ulong) dbValue;
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ public static bool TryGetSByte(this DbDataReader rs, int ordinal, out sbyte value)
+ {
+ var dbValue = rs[ordinal];
+
+ if (dbValue is sbyte)
+ {
+ value = (sbyte) rs[ordinal];
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ public static bool TryGetString(this DbDataReader rs, int ordinal, out string value)
+ {
+ try
+ {
+ value = rs.GetString(ordinal);
+ return true;
+ }
+ catch (Exception)
+ {
+ value = default;
+ return false;
+ }
+ }
+
+ public static bool TryGetTimeSpan(this DbDataReader rs, int ordinal, out TimeSpan value)
+ {
+ var dbValue = rs[ordinal];
+
+ if (dbValue is TimeSpan)
+ {
+ value = (TimeSpan) dbValue;
+ return true;
+ }
+
+ value = default;
+ return false;
+ }
+
+ public static object[] GetValues(this DbDataReader rs, System.Type[] types)
+ {
+ if (types.Length != rs.FieldCount)
+ {
+ throw new InvalidOperationException("Exptected number of types does not match the number of fields.");
+ }
+
+ var values = new object[rs.FieldCount];
+
+ for (var i = 0; i < rs.FieldCount; i++)
+ {
+ var typeCode = System.Type.GetTypeCode(types[i]);
+
+ values[i] = typeCode switch
+ {
+ TypeCode.Boolean => rs.GetBoolean(i),
+ TypeCode.Char => rs.GetChar(i),
+ TypeCode.Byte => rs.GetByte(i),
+ TypeCode.Int16 => rs.GetInt16(i),
+ TypeCode.Int32 => rs.GetInt32(i),
+ TypeCode.Int64 => rs.GetInt64(i),
+ TypeCode.Single => rs.GetFloat(i),
+ TypeCode.Double => rs.GetDouble(i),
+ TypeCode.Decimal => rs.GetDecimal(i),
+ TypeCode.DateTime => rs.GetDateTime(i),
+ TypeCode.String => rs.GetString(i),
+ TypeCode.UInt16 => (ushort) rs[i],
+ TypeCode.UInt32 => (uint) rs[i],
+ TypeCode.UInt64 => (ulong) rs[i],
+ _ => rs[i]
+ };
+ }
+
+ return values;
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/DbDataReaderWrapper.cs b/src/NHibernate/AdoNet/DbDataReaderWrapper.cs
new file mode 100644
index 00000000000..a44f67d5c27
--- /dev/null
+++ b/src/NHibernate/AdoNet/DbDataReaderWrapper.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections;
+using System.Data.Common;
+
+namespace NHibernate.AdoNet
+{
+ public abstract partial class DbDataReaderWrapper : DbDataReader
+ {
+ protected DbDataReader DataReader { get; private set; }
+
+ public override int Depth => DataReader.Depth;
+
+ public override int FieldCount => DataReader.FieldCount;
+
+ public override bool HasRows => DataReader.HasRows;
+
+ public override bool IsClosed => DataReader.IsClosed;
+
+ public override int RecordsAffected => DataReader.RecordsAffected;
+
+ public override object this[string name] => DataReader[name];
+
+ public override object this[int ordinal] => DataReader[ordinal];
+
+ public DbDataReaderWrapper(DbDataReader dbDataReader)
+ {
+ DataReader = dbDataReader;
+ }
+
+ public override bool GetBoolean(int ordinal)
+ {
+ return DataReader.GetBoolean(ordinal);
+ }
+
+ public override byte GetByte(int ordinal)
+ {
+ return DataReader.GetByte(ordinal);
+ }
+
+ public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
+ {
+ return DataReader.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
+ }
+
+ public override char GetChar(int ordinal)
+ {
+ return DataReader.GetChar(ordinal);
+ }
+
+ public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
+ {
+ return DataReader.GetChars(ordinal, dataOffset, buffer, bufferOffset, length);
+ }
+
+ public override string GetDataTypeName(int ordinal)
+ {
+ return DataReader.GetDataTypeName(ordinal);
+ }
+
+ public override DateTime GetDateTime(int ordinal)
+ {
+ return DataReader.GetDateTime(ordinal);
+ }
+
+ public override decimal GetDecimal(int ordinal)
+ {
+ return DataReader.GetDecimal(ordinal);
+ }
+
+ public override double GetDouble(int ordinal)
+ {
+ return DataReader.GetDouble(ordinal);
+ }
+
+ public override IEnumerator GetEnumerator()
+ {
+ while (Read())
+ {
+ yield return this;
+ }
+ }
+
+ public override System.Type GetFieldType(int ordinal)
+ {
+ return DataReader.GetFieldType(ordinal);
+ }
+
+ public override float GetFloat(int ordinal)
+ {
+ return DataReader.GetFloat(ordinal);
+ }
+
+ public override Guid GetGuid(int ordinal)
+ {
+ return DataReader.GetGuid(ordinal);
+ }
+
+ public override short GetInt16(int ordinal)
+ {
+ return DataReader.GetInt16(ordinal);
+ }
+
+ public override int GetInt32(int ordinal)
+ {
+ return DataReader.GetInt32(ordinal);
+ }
+
+ public override long GetInt64(int ordinal)
+ {
+ return DataReader.GetInt64(ordinal);
+ }
+
+ public override string GetName(int ordinal)
+ {
+ return DataReader.GetName(ordinal);
+ }
+
+ public override int GetOrdinal(string name)
+ {
+ return DataReader.GetOrdinal(name);
+ }
+
+ public override string GetString(int ordinal)
+ {
+ return DataReader.GetString(ordinal);
+ }
+
+ public override object GetValue(int ordinal)
+ {
+ return DataReader.GetValue(ordinal);
+ }
+
+ public override int GetValues(object[] values)
+ {
+ return DataReader.GetValues(values);
+ }
+
+ public override bool IsDBNull(int ordinal)
+ {
+ return DataReader.IsDBNull(ordinal);
+ }
+
+ public override bool NextResult()
+ {
+ return DataReader.NextResult();
+ }
+
+ public override bool Read()
+ {
+ return DataReader.Read();
+ }
+
+ public override void Close()
+ {
+ DataReader.Close();
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/DirectCastDbDataReader.cs b/src/NHibernate/AdoNet/DirectCastDbDataReader.cs
new file mode 100644
index 00000000000..5848323fbc2
--- /dev/null
+++ b/src/NHibernate/AdoNet/DirectCastDbDataReader.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections;
+using System.Data.Common;
+using System.Diagnostics.CodeAnalysis;
+
+namespace NHibernate.AdoNet
+{
+ public partial class DirectCastDbDataReader : DbDataReaderWrapper
+ {
+ public DirectCastDbDataReader(DbDataReader dbDataReader) : base(dbDataReader) { }
+
+ public override object this[int ordinal] => DataReader[ordinal];
+
+ public override object this[string name] => DataReader[name];
+
+ public override int Depth => DataReader.Depth;
+
+ public override int FieldCount => DataReader.FieldCount;
+
+ public override bool HasRows => DataReader.HasRows;
+
+ public override bool IsClosed => DataReader.IsClosed;
+
+ public override int RecordsAffected => DataReader.RecordsAffected;
+
+ public override bool GetBoolean(int ordinal)
+ {
+ return (bool) DataReader[ordinal];
+ }
+
+ public override byte GetByte(int ordinal)
+ {
+ return (byte) DataReader[ordinal];
+ }
+
+ public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
+ {
+ return DataReader.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
+ }
+
+ public override char GetChar(int ordinal)
+ {
+ return (char) DataReader[ordinal];
+ }
+
+ public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
+ {
+ return DataReader.GetChars(ordinal, dataOffset, buffer, bufferOffset, length);
+ }
+
+ public override string GetDataTypeName(int ordinal)
+ {
+ return DataReader.GetDataTypeName(ordinal);
+ }
+
+ public override DateTime GetDateTime(int ordinal)
+ {
+ return (DateTime) DataReader[ordinal];
+ }
+
+ public override decimal GetDecimal(int ordinal)
+ {
+ return (decimal) DataReader[ordinal];
+ }
+
+ public override double GetDouble(int ordinal)
+ {
+ return (double) DataReader[ordinal];
+ }
+
+ public override IEnumerator GetEnumerator()
+ {
+ while (DataReader.Read())
+ {
+ yield return this;
+ }
+ }
+
+ public override System.Type GetFieldType(int ordinal)
+ {
+ return DataReader.GetFieldType(ordinal);
+ }
+
+ public override float GetFloat(int ordinal)
+ {
+ return (float) DataReader[ordinal];
+ }
+
+ public override Guid GetGuid(int ordinal)
+ {
+ return (Guid) DataReader[ordinal];
+ }
+
+ public override short GetInt16(int ordinal)
+ {
+ return (short) DataReader[ordinal];
+ }
+
+ public override int GetInt32(int ordinal)
+ {
+ return (int) DataReader[ordinal];
+ }
+
+ public override long GetInt64(int ordinal)
+ {
+ return (long) DataReader[ordinal];
+ }
+
+ public override string GetName(int ordinal)
+ {
+ return DataReader.GetName(ordinal);
+ }
+
+ public override int GetOrdinal(string name)
+ {
+ return DataReader.GetOrdinal(name);
+ }
+
+ public override string GetString(int ordinal)
+ {
+ return (string) DataReader[ordinal];
+ }
+
+ public override object GetValue(int ordinal)
+ {
+ return DataReader.GetValue(ordinal);
+ }
+
+ public override int GetValues(object[] values)
+ {
+ return DataReader.GetValues(values);
+ }
+
+ public override bool IsDBNull(int ordinal)
+ {
+ return DataReader.IsDBNull(ordinal);
+ }
+
+ public override bool NextResult()
+ {
+ return DataReader.NextResult();
+ }
+
+ public override bool Read()
+ {
+ return DataReader.Read();
+ }
+
+ public override void Close()
+ {
+ DataReader.Close();
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/FirebirdDbDataReader.cs b/src/NHibernate/AdoNet/FirebirdDbDataReader.cs
new file mode 100644
index 00000000000..9ff2940b85c
--- /dev/null
+++ b/src/NHibernate/AdoNet/FirebirdDbDataReader.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NHibernate.Id.Insert;
+
+namespace NHibernate.AdoNet
+{
+ public class FirebirdDbDataReader : DbDataReaderWrapper
+ {
+ public FirebirdDbDataReader(DbDataReader reader) : base(reader) { }
+
+ public override DateTime GetDateTime(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string s => DateTime.Parse(s, CultureInfo.InvariantCulture),
+ _ => (DateTime) value
+ };
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/MySqlDbDataReader.cs b/src/NHibernate/AdoNet/MySqlDbDataReader.cs
new file mode 100644
index 00000000000..bf28218dfcb
--- /dev/null
+++ b/src/NHibernate/AdoNet/MySqlDbDataReader.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NHibernate.AdoNet
+{
+ public class MySqlDbDataReader : DbDataReaderWrapper
+ {
+ public MySqlDbDataReader(DbDataReader reader) : base(reader) { }
+
+ // MySql driver has a bug that incorrectly uses the CurrentCulture to parse strings.
+ public override float GetFloat(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string s => float.Parse(s, CultureInfo.InvariantCulture),
+ _ => (float) value
+ };
+ }
+
+ public override double GetDouble(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string s => double.Parse(s, CultureInfo.InvariantCulture),
+ _ => (double) value
+ };
+ }
+
+ public override decimal GetDecimal(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string s => decimal.Parse(s, CultureInfo.InvariantCulture),
+ _ => (decimal) value
+ };
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/NoCharDbDataReader.cs b/src/NHibernate/AdoNet/NoCharDbDataReader.cs
new file mode 100644
index 00000000000..d7d4860b923
--- /dev/null
+++ b/src/NHibernate/AdoNet/NoCharDbDataReader.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NHibernate.AdoNet
+{
+ ///
+ /// Many database drivers lack support for DbDataReader.GetChar and throw a
+ /// NotSupportedException. This reader provides an implementation on top of
+ /// the indexer method for defficient drivers.
+ ///
+ public class NoCharDbDataReader : DbDataReaderWrapper
+ {
+ public NoCharDbDataReader(DbDataReader reader) : base(reader) { }
+
+ public override char GetChar(int ordinal)
+ {
+ // The underlying DataReader does not support the GetChar method.
+ // Use the indexer to obtain the value and convert it to a char if necessary.
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string { Length: > 0 } s => s[0],
+ _ => (char) value
+ };
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/OracleDbDataReader.cs b/src/NHibernate/AdoNet/OracleDbDataReader.cs
new file mode 100644
index 00000000000..2ea16582e1d
--- /dev/null
+++ b/src/NHibernate/AdoNet/OracleDbDataReader.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Data.Common;
+
+namespace NHibernate.AdoNet
+{
+ public class OracleDbDataReader : DbDataReaderWrapper
+ {
+ private readonly string _timestampFormat;
+
+ public OracleDbDataReader(DbDataReader reader, string timestampFormat)
+ : base(reader)
+ {
+ _timestampFormat = timestampFormat;
+ }
+
+ // Oracle driver does not implement GetChar
+ public override char GetChar(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string { Length: > 0 } s => s[0],
+ _ => (char) value
+ };
+ }
+
+ public override DateTime GetDateTime(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ if (value is string && _timestampFormat != null)
+ {
+ return ParseDate((string)value);
+ }
+
+ return (DateTime) value;
+ }
+
+ private DateTime ParseDate(string value)
+ {
+ // Need to implment rules according to https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/Format-Models.html#GUID-49B32A81-0904-433E-B7FE-51606672183A
+ throw new NotImplementedException($"Should parse '{value}' using '{_timestampFormat}'");
+ }
+ }
+}
diff --git a/src/NHibernate/AdoNet/SqlAnywhereDbDataReader.cs b/src/NHibernate/AdoNet/SqlAnywhereDbDataReader.cs
new file mode 100644
index 00000000000..7c326a49012
--- /dev/null
+++ b/src/NHibernate/AdoNet/SqlAnywhereDbDataReader.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Data.Common;
+using System.Globalization;
+
+namespace NHibernate.AdoNet
+{
+ ///
+ /// This is a wrapper for the DbDataReader returned by the SqlAnywhere driver.
+ /// The DbDataReader in the SqlAnywhere driver does not support the GetChar method,
+ /// and uses Convert.To* without specifying an .
+ ///
+ public class SqlAnywhereDbDataReader : NoCharDbDataReader
+ {
+ public SqlAnywhereDbDataReader(DbDataReader reader) : base(reader) { }
+
+ public override float GetFloat(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string s => float.Parse(s, CultureInfo.InvariantCulture),
+ _ => (float) value
+ };
+ }
+
+ public override double GetDouble(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string s => double.Parse(s, CultureInfo.InvariantCulture),
+ _ => (double) value
+ };
+ }
+
+ public override decimal GetDecimal(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string s => decimal.Parse(s, CultureInfo.InvariantCulture),
+ _ => (decimal) value
+ };
+ }
+
+ public override DateTime GetDateTime(int ordinal)
+ {
+ var value = DataReader[ordinal];
+
+ return value switch
+ {
+ string s => DateTime.Parse(s, CultureInfo.InvariantCulture),
+ _ => (DateTime) value
+ };
+ }
+ }
+}
diff --git a/src/NHibernate/Async/AdoNet/AbstractBatcher.cs b/src/NHibernate/Async/AdoNet/AbstractBatcher.cs
index 21168ef3ea6..d376a232258 100644
--- a/src/NHibernate/Async/AdoNet/AbstractBatcher.cs
+++ b/src/NHibernate/Async/AdoNet/AbstractBatcher.cs
@@ -167,7 +167,7 @@ private async Task DoExecuteReaderAsync(DbCommand cmd, Cancellatio
cancellationToken.ThrowIfCancellationRequested();
try
{
- var reader = await (cmd.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+ var reader = await (Driver.ExecuteReaderAsync(cmd, cancellationToken)).ConfigureAwait(false);
if (reader == null)
{
// MySql may return null instead of an exception, by example when the query is canceled by another thread.
diff --git a/src/NHibernate/Async/AdoNet/DbDataReaderWrapper.cs b/src/NHibernate/Async/AdoNet/DbDataReaderWrapper.cs
new file mode 100644
index 00000000000..b4ee08f230e
--- /dev/null
+++ b/src/NHibernate/Async/AdoNet/DbDataReaderWrapper.cs
@@ -0,0 +1,40 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Collections;
+using System.Data.Common;
+
+namespace NHibernate.AdoNet
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public abstract partial class DbDataReaderWrapper : DbDataReader
+ {
+
+ public override Task NextResultAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ return DataReader.NextResultAsync(cancellationToken);
+ }
+
+ public override Task ReadAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ return DataReader.ReadAsync(cancellationToken);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/AdoNet/DirectCastDbDataReader.cs b/src/NHibernate/Async/AdoNet/DirectCastDbDataReader.cs
new file mode 100644
index 00000000000..2f325960938
--- /dev/null
+++ b/src/NHibernate/Async/AdoNet/DirectCastDbDataReader.cs
@@ -0,0 +1,41 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Collections;
+using System.Data.Common;
+using System.Diagnostics.CodeAnalysis;
+
+namespace NHibernate.AdoNet
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class DirectCastDbDataReader : DbDataReaderWrapper
+ {
+
+ public override Task NextResultAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ return DataReader.NextResultAsync(cancellationToken);
+ }
+
+ public override Task ReadAsync(CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ return DataReader.ReadAsync(cancellationToken);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/DriverBase.cs b/src/NHibernate/Async/Driver/DriverBase.cs
new file mode 100644
index 00000000000..15ef16f9175
--- /dev/null
+++ b/src/NHibernate/Async/Driver/DriverBase.cs
@@ -0,0 +1,44 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Linq;
+using NHibernate.Engine;
+using NHibernate.SqlCommand;
+using NHibernate.SqlTypes;
+using NHibernate.Util;
+using Environment = NHibernate.Cfg.Environment;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public abstract partial class DriverBase : IDriver, ISqlParameterFormatter
+ {
+
+ #if NETFX
+ #else
+
+ #endif
+
+ ///
+ public virtual Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ return command.ExecuteReaderAsync(cancellationToken);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/DriverExtensions.cs b/src/NHibernate/Async/Driver/DriverExtensions.cs
new file mode 100644
index 00000000000..c9009f022df
--- /dev/null
+++ b/src/NHibernate/Async/Driver/DriverExtensions.cs
@@ -0,0 +1,49 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Data;
+using System.Data.Common;
+using NHibernate.AdoNet;
+using NHibernate.SqlTypes;
+using NHibernate.Util;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public static partial class DriverExtensions
+ {
+
+ // 6.0 TODO: merge into IDriver
+ ///
+ /// Executes the command and returns a .
+ ///
+ /// The driver.
+ /// The command to execute.
+ /// A cancellation token that can be used to cancel the work
+ /// A DbDataReader
+ public static Task ExecuteReaderAsync(this IDriver driver, DbCommand command, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ try
+ {
+ return driver is DriverBase driverBase ? driverBase.ExecuteReaderAsync(command, cancellationToken) : command.ExecuteReaderAsync(cancellationToken);
+ }
+ catch (System.Exception ex)
+ {
+ return Task.FromException(ex);
+ }
+ }
+
+ }
+}
diff --git a/src/NHibernate/Async/Driver/FirebirdClientDriver.cs b/src/NHibernate/Async/Driver/FirebirdClientDriver.cs
new file mode 100644
index 00000000000..86ad67e1bad
--- /dev/null
+++ b/src/NHibernate/Async/Driver/FirebirdClientDriver.cs
@@ -0,0 +1,40 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using NHibernate.AdoNet;
+using NHibernate.Dialect;
+using NHibernate.SqlCommand;
+using NHibernate.SqlTypes;
+using NHibernate.Util;
+using Environment = NHibernate.Cfg.Environment;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class FirebirdClientDriver : ReflectionBasedDriver
+ {
+
+ public override async Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ return new FirebirdDbDataReader(reader);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/MicrosoftDataSqlClientDriver.cs b/src/NHibernate/Async/Driver/MicrosoftDataSqlClientDriver.cs
new file mode 100644
index 00000000000..3061119973e
--- /dev/null
+++ b/src/NHibernate/Async/Driver/MicrosoftDataSqlClientDriver.cs
@@ -0,0 +1,36 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.Common;
+using NHibernate.AdoNet;
+using NHibernate.Dialect;
+using NHibernate.Engine;
+using NHibernate.SqlTypes;
+using NHibernate.Util;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class MicrosoftDataSqlClientDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider, IParameterAdjuster
+ {
+
+ public override async Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ return new NoCharDbDataReader(reader);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/MySqlDataDriver.cs b/src/NHibernate/Async/Driver/MySqlDataDriver.cs
new file mode 100644
index 00000000000..870925035e7
--- /dev/null
+++ b/src/NHibernate/Async/Driver/MySqlDataDriver.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Data.Common;
+using NHibernate.AdoNet;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class MySqlDataDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
+ {
+
+ public override async Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ return reader != null ? new MySqlDbDataReader(reader) : null;
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs b/src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs
index 67979f576a1..cd50b7526a4 100644
--- a/src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs
+++ b/src/NHibernate/Async/Driver/OracleDataClientDriverBase.cs
@@ -12,8 +12,6 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
-using System.Threading;
-using System.Threading.Tasks;
using NHibernate.AdoNet;
using NHibernate.Engine.Query;
using NHibernate.SqlTypes;
@@ -21,18 +19,35 @@
namespace NHibernate.Driver
{
+ using System.Threading.Tasks;
+ using System.Threading;
public abstract partial class OracleDataClientDriverBase : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
{
- private partial class OracleDbCommandWrapper : DbCommandWrapper
- {
- protected override async Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
+ public override Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ if (!SuppressDecimalInvalidCastException && _suppressDecimalInvalidCastExceptionSetter == null)
{
- cancellationToken.ThrowIfCancellationRequested();
- var reader = await (Command.ExecuteReaderAsync(behavior, cancellationToken)).ConfigureAwait(false);
- _suppressDecimalInvalidCastExceptionSetter(reader, true);
+ throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer");
+ }
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ return InternalExecuteReaderAsync();
+ async Task InternalExecuteReaderAsync()
+ {
+
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ if (SuppressDecimalInvalidCastException)
+ {
+ _suppressDecimalInvalidCastExceptionSetter(reader, true);
+ }
+
+ string timestampFormat = GetDateFormat(command.Connection);
- return reader;
+ return new OracleDbDataReader(reader, timestampFormat);
}
}
}
diff --git a/src/NHibernate/Async/Driver/SapSQLAnywhere17Driver.cs b/src/NHibernate/Async/Driver/SapSQLAnywhere17Driver.cs
new file mode 100644
index 00000000000..cee3d4cd4cb
--- /dev/null
+++ b/src/NHibernate/Async/Driver/SapSQLAnywhere17Driver.cs
@@ -0,0 +1,29 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Data.Common;
+using NHibernate.AdoNet;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class SapSQLAnywhere17Driver : ReflectionBasedDriver
+ {
+
+ public override async Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ return new SqlAnywhereDbDataReader(reader);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/Sql2008ClientDriver.cs b/src/NHibernate/Async/Driver/Sql2008ClientDriver.cs
new file mode 100644
index 00000000000..d714784b8c3
--- /dev/null
+++ b/src/NHibernate/Async/Driver/Sql2008ClientDriver.cs
@@ -0,0 +1,37 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Data;
+using System.Data.Common;
+using NHibernate.Util;
+using NHibernate.AdoNet;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class Sql2008ClientDriver : SqlClientDriver
+ {
+
+ #if NETFX
+ #else
+
+ #endif
+
+ public override async Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ return new NoCharDbDataReader(reader);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/SqlServerCeDriver.cs b/src/NHibernate/Async/Driver/SqlServerCeDriver.cs
new file mode 100644
index 00000000000..2fe8bc1a65e
--- /dev/null
+++ b/src/NHibernate/Async/Driver/SqlServerCeDriver.cs
@@ -0,0 +1,33 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System;
+using System.Data;
+using System.Data.Common;
+using NHibernate.AdoNet;
+using NHibernate.SqlTypes;
+using NHibernate.Util;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class SqlServerCeDriver : ReflectionBasedDriver
+ {
+
+ public override async Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ return new NoCharDbDataReader(reader);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/SybaseSQLAnywhereDotNet4Driver.cs b/src/NHibernate/Async/Driver/SybaseSQLAnywhereDotNet4Driver.cs
new file mode 100644
index 00000000000..04a88038dec
--- /dev/null
+++ b/src/NHibernate/Async/Driver/SybaseSQLAnywhereDotNet4Driver.cs
@@ -0,0 +1,29 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Data.Common;
+using NHibernate.AdoNet;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class SybaseSQLAnywhereDotNet4Driver : ReflectionBasedDriver
+ {
+
+ public override async Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ return new SqlAnywhereDbDataReader(reader);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Driver/SybaseSQLAnywhereDriver.cs b/src/NHibernate/Async/Driver/SybaseSQLAnywhereDriver.cs
new file mode 100644
index 00000000000..d35735b1262
--- /dev/null
+++ b/src/NHibernate/Async/Driver/SybaseSQLAnywhereDriver.cs
@@ -0,0 +1,29 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Data.Common;
+using NHibernate.AdoNet;
+
+namespace NHibernate.Driver
+{
+ using System.Threading.Tasks;
+ using System.Threading;
+ public partial class SybaseSQLAnywhereDriver : ReflectionBasedDriver
+ {
+
+ public override async Task ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var reader = await (command.ExecuteReaderAsync(cancellationToken)).ConfigureAwait(false);
+
+ return new SqlAnywhereDbDataReader(reader);
+ }
+ }
+}
diff --git a/src/NHibernate/Async/Type/AbstractDateTimeType.cs b/src/NHibernate/Async/Type/AbstractDateTimeType.cs
index 827ac034995..2265da31919 100644
--- a/src/NHibernate/Async/Type/AbstractDateTimeType.cs
+++ b/src/NHibernate/Async/Type/AbstractDateTimeType.cs
@@ -13,6 +13,7 @@
using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/ByteType.cs b/src/NHibernate/Async/Type/ByteType.cs
index 0d38fdf1a71..bc2c01e193e 100644
--- a/src/NHibernate/Async/Type/ByteType.cs
+++ b/src/NHibernate/Async/Type/ByteType.cs
@@ -13,6 +13,7 @@
using System.Data;
using System.Data.Common;
using System.Numerics;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/Int16Type.cs b/src/NHibernate/Async/Type/Int16Type.cs
index 598f83c5aa5..3f35f525f6d 100644
--- a/src/NHibernate/Async/Type/Int16Type.cs
+++ b/src/NHibernate/Async/Type/Int16Type.cs
@@ -16,6 +16,7 @@
using System.Collections.Generic;
using System.Data;
using System.Numerics;
+using NHibernate.AdoNet;
namespace NHibernate.Type
{
diff --git a/src/NHibernate/Async/Type/Int32Type.cs b/src/NHibernate/Async/Type/Int32Type.cs
index 3b4217968dd..43f736e3a99 100644
--- a/src/NHibernate/Async/Type/Int32Type.cs
+++ b/src/NHibernate/Async/Type/Int32Type.cs
@@ -16,6 +16,7 @@
using System.Collections.Generic;
using System.Data;
using System.Numerics;
+using NHibernate.AdoNet;
namespace NHibernate.Type
{
diff --git a/src/NHibernate/Async/Type/Int64Type.cs b/src/NHibernate/Async/Type/Int64Type.cs
index e1b07967cbd..4ed4d25cbb5 100644
--- a/src/NHibernate/Async/Type/Int64Type.cs
+++ b/src/NHibernate/Async/Type/Int64Type.cs
@@ -14,6 +14,7 @@
using System.Data;
using System.Data.Common;
using System.Numerics;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/TicksType.cs b/src/NHibernate/Async/Type/TicksType.cs
index 0ffb06b37f6..c239faf2c88 100644
--- a/src/NHibernate/Async/Type/TicksType.cs
+++ b/src/NHibernate/Async/Type/TicksType.cs
@@ -11,6 +11,7 @@
using System;
using System.Data;
using System.Data.Common;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/TimeAsTimeSpanType.cs b/src/NHibernate/Async/Type/TimeAsTimeSpanType.cs
index 185baa88eba..08aedfa9381 100644
--- a/src/NHibernate/Async/Type/TimeAsTimeSpanType.cs
+++ b/src/NHibernate/Async/Type/TimeAsTimeSpanType.cs
@@ -15,6 +15,7 @@
using NHibernate.SqlTypes;
using System.Collections.Generic;
using System.Data;
+using NHibernate.AdoNet;
namespace NHibernate.Type
{
diff --git a/src/NHibernate/Async/Type/TimeSpanType.cs b/src/NHibernate/Async/Type/TimeSpanType.cs
index 0917413671c..b5d7339ef79 100644
--- a/src/NHibernate/Async/Type/TimeSpanType.cs
+++ b/src/NHibernate/Async/Type/TimeSpanType.cs
@@ -15,6 +15,7 @@
using NHibernate.SqlTypes;
using System.Collections.Generic;
using System.Data;
+using NHibernate.AdoNet;
namespace NHibernate.Type
{
diff --git a/src/NHibernate/Async/Type/UInt16Type.cs b/src/NHibernate/Async/Type/UInt16Type.cs
index cfd05ee60f6..7d3a63d3a55 100644
--- a/src/NHibernate/Async/Type/UInt16Type.cs
+++ b/src/NHibernate/Async/Type/UInt16Type.cs
@@ -14,6 +14,7 @@
using System.Data;
using System.Data.Common;
using System.Numerics;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/UInt32Type.cs b/src/NHibernate/Async/Type/UInt32Type.cs
index 2fa37d01162..5ff4642c8da 100644
--- a/src/NHibernate/Async/Type/UInt32Type.cs
+++ b/src/NHibernate/Async/Type/UInt32Type.cs
@@ -14,6 +14,7 @@
using System.Data;
using System.Data.Common;
using System.Numerics;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/UInt64Type.cs b/src/NHibernate/Async/Type/UInt64Type.cs
index d2db5e47c99..629f981f4cc 100644
--- a/src/NHibernate/Async/Type/UInt64Type.cs
+++ b/src/NHibernate/Async/Type/UInt64Type.cs
@@ -14,6 +14,7 @@
using System.Data;
using System.Data.Common;
using System.Numerics;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/UriType.cs b/src/NHibernate/Async/Type/UriType.cs
index ff7e316ffd1..49013ef70c7 100644
--- a/src/NHibernate/Async/Type/UriType.cs
+++ b/src/NHibernate/Async/Type/UriType.cs
@@ -10,6 +10,7 @@
using System;
using System.Data.Common;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/XDocType.cs b/src/NHibernate/Async/Type/XDocType.cs
index 72da2d2b271..2c1c6191a0e 100644
--- a/src/NHibernate/Async/Type/XDocType.cs
+++ b/src/NHibernate/Async/Type/XDocType.cs
@@ -11,6 +11,7 @@
using System;
using System.Data.Common;
using System.Xml.Linq;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Async/Type/XmlDocType.cs b/src/NHibernate/Async/Type/XmlDocType.cs
index 87da089118a..9a0e550d4ae 100644
--- a/src/NHibernate/Async/Type/XmlDocType.cs
+++ b/src/NHibernate/Async/Type/XmlDocType.cs
@@ -11,6 +11,7 @@
using System;
using System.Data.Common;
using System.Xml;
+using NHibernate.AdoNet;
using NHibernate.Engine;
using NHibernate.SqlTypes;
diff --git a/src/NHibernate/Cfg/Environment.cs b/src/NHibernate/Cfg/Environment.cs
index c052cd64267..ca8b577b049 100644
--- a/src/NHibernate/Cfg/Environment.cs
+++ b/src/NHibernate/Cfg/Environment.cs
@@ -92,6 +92,8 @@ public static string Version
/// A default database catalog name to use for unqualified tablenames
public const string DefaultCatalog = "default_catalog";
+ public const string Locale = "locale";
+
// Since v5
[Obsolete("DefaultEntityMode is deprecated.")]
public const string DefaultEntityMode = "default_entity_mode";
diff --git a/src/NHibernate/Cfg/Settings.cs b/src/NHibernate/Cfg/Settings.cs
index 633133450a8..2eda1996bee 100644
--- a/src/NHibernate/Cfg/Settings.cs
+++ b/src/NHibernate/Cfg/Settings.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
+using System.Globalization;
using System.Linq.Expressions;
using NHibernate.AdoNet;
using NHibernate.AdoNet.Util;
@@ -228,5 +229,6 @@ internal string GetFullCacheRegionName(string name)
public BatchFetchStyle BatchFetchStyle { get; internal set; }
public BatchingEntityLoaderBuilder BatchingEntityLoaderBuilder { get; internal set; }
public BatchingCollectionInitializerBuilder BatchingCollectionInitializationBuilder { get; internal set; }
+ public CultureInfo Locale { get; internal set; }
}
}
diff --git a/src/NHibernate/Cfg/SettingsFactory.cs b/src/NHibernate/Cfg/SettingsFactory.cs
index 4babe5afb93..a09e513703a 100644
--- a/src/NHibernate/Cfg/SettingsFactory.cs
+++ b/src/NHibernate/Cfg/SettingsFactory.cs
@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Data;
+using System.Globalization;
using NHibernate.AdoNet;
using NHibernate.AdoNet.Util;
using NHibernate.Cache;
@@ -354,6 +355,9 @@ public Settings BuildSettings(IDictionary properties)
settings.BatchFetchStyle = PropertiesHelper.GetEnum(Environment.BatchFetchStyle, properties, BatchFetchStyle.Legacy);
settings.BatchingEntityLoaderBuilder = GetBatchingEntityLoaderBuilder(settings.BatchFetchStyle);
settings.BatchingCollectionInitializationBuilder = GetBatchingCollectionInitializationBuilder(settings.BatchFetchStyle);
+
+ string locale = PropertiesHelper.GetString(Environment.Locale, properties, CultureInfo.InvariantCulture.Name);
+ settings.Locale = CultureInfo.GetCultureInfo(locale);
return settings;
}
diff --git a/src/NHibernate/Driver/DriverBase.cs b/src/NHibernate/Driver/DriverBase.cs
index be5c81366e1..eae4e06ddde 100644
--- a/src/NHibernate/Driver/DriverBase.cs
+++ b/src/NHibernate/Driver/DriverBase.cs
@@ -14,7 +14,7 @@ namespace NHibernate.Driver
///
/// Base class for the implementation of IDriver
///
- public abstract class DriverBase : IDriver, ISqlParameterFormatter
+ public abstract partial class DriverBase : IDriver, ISqlParameterFormatter
{
private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(DriverBase));
@@ -361,5 +361,11 @@ public DbParameter GenerateOutputParameter(DbCommand command)
/// Get the timeout in seconds for ADO.NET queries.
///
public virtual int CommandTimeout => commandTimeout;
+
+ ///
+ public virtual DbDataReader ExecuteReader(DbCommand command)
+ {
+ return command.ExecuteReader();
+ }
}
}
diff --git a/src/NHibernate/Driver/DriverExtensions.cs b/src/NHibernate/Driver/DriverExtensions.cs
index d370269c35e..dc624e8e4d6 100644
--- a/src/NHibernate/Driver/DriverExtensions.cs
+++ b/src/NHibernate/Driver/DriverExtensions.cs
@@ -6,7 +6,7 @@
namespace NHibernate.Driver
{
- public static class DriverExtensions
+ public static partial class DriverExtensions
{
internal static void AdjustParameterForValue(this IDriver driver, DbParameter parameter, SqlType sqlType, object value)
{
@@ -60,5 +60,18 @@ public static DbCommand UnwrapDbCommand(this IDriver driver, DbCommand command)
{
return driver is DriverBase driverBase ? driverBase.UnwrapDbCommand(command) : command;
}
+
+ // 6.0 TODO: merge into IDriver
+ ///
+ /// Executes the command and returns a .
+ ///
+ /// The driver.
+ /// The command to execute.
+ /// A DbDataReader
+ public static DbDataReader ExecuteReader(this IDriver driver, DbCommand command)
+ {
+ return driver is DriverBase driverBase ? driverBase.ExecuteReader(command) : command.ExecuteReader();
+ }
+
}
}
diff --git a/src/NHibernate/Driver/FirebirdClientDriver.cs b/src/NHibernate/Driver/FirebirdClientDriver.cs
index e2859880adb..6b67e567dab 100644
--- a/src/NHibernate/Driver/FirebirdClientDriver.cs
+++ b/src/NHibernate/Driver/FirebirdClientDriver.cs
@@ -5,6 +5,7 @@
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
+using NHibernate.AdoNet;
using NHibernate.Dialect;
using NHibernate.SqlCommand;
using NHibernate.SqlTypes;
@@ -17,7 +18,7 @@ namespace NHibernate.Driver
/// A NHibernate Driver for using the Firebird data provider located in
/// FirebirdSql.Data.FirebirdClient assembly.
///
- public class FirebirdClientDriver : ReflectionBasedDriver
+ public partial class FirebirdClientDriver : ReflectionBasedDriver
{
private const string SELECT_CLAUSE_EXP = @"(?<=\bselect\b|\bwhere\b).*";
private const string CAST_PARAMS_EXP =
@@ -212,5 +213,12 @@ public void ClearPool(string connectionString)
/// See http://tracker.firebirdsql.org/browse/DNET-766.
///
public override bool SupportsEnlistmentWhenAutoEnlistmentIsDisabled => false;
+
+ public override DbDataReader ExecuteReader(DbCommand command)
+ {
+ var reader = command.ExecuteReader();
+
+ return new FirebirdDbDataReader(reader);
+ }
}
}
diff --git a/src/NHibernate/Driver/MicrosoftDataSqlClientDriver.cs b/src/NHibernate/Driver/MicrosoftDataSqlClientDriver.cs
index e2e12674e3d..4c150e85a4e 100644
--- a/src/NHibernate/Driver/MicrosoftDataSqlClientDriver.cs
+++ b/src/NHibernate/Driver/MicrosoftDataSqlClientDriver.cs
@@ -13,7 +13,7 @@ namespace NHibernate.Driver
///
/// A NHibernate Driver for using the SqlClient DataProvider
///
- public class MicrosoftDataSqlClientDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider, IParameterAdjuster
+ public partial class MicrosoftDataSqlClientDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider, IParameterAdjuster
{
const byte MaxTime = 5;
@@ -217,5 +217,12 @@ public override IResultSetsCommand GetResultSetsCommand(ISessionImplementor sess
{
return new BasicResultSetsCommand(session);
}
+
+ public override DbDataReader ExecuteReader(DbCommand command)
+ {
+ var reader = command.ExecuteReader();
+
+ return new NoCharDbDataReader(reader);
+ }
}
}
diff --git a/src/NHibernate/Driver/MySqlDataDriver.cs b/src/NHibernate/Driver/MySqlDataDriver.cs
index efbe9675f9a..90cfb4ea515 100644
--- a/src/NHibernate/Driver/MySqlDataDriver.cs
+++ b/src/NHibernate/Driver/MySqlDataDriver.cs
@@ -1,4 +1,5 @@
using System;
+using System.Data.Common;
using NHibernate.AdoNet;
namespace NHibernate.Driver
@@ -17,7 +18,7 @@ namespace NHibernate.Driver
/// for any updates and/or documentation regarding MySQL.
///
///
- public class MySqlDataDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
+ public partial class MySqlDataDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
{
///
/// Initializes a new instance of the class.
@@ -79,6 +80,13 @@ public override IResultSetsCommand GetResultSetsCommand(Engine.ISessionImplement
///
public override DateTime MinDate => new DateTime(1000, 1, 1);
+ public override DbDataReader ExecuteReader(DbCommand command)
+ {
+ var reader = command.ExecuteReader();
+
+ return reader != null ? new MySqlDbDataReader(reader) : null;
+ }
+
System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass => typeof(MySqlClientBatchingBatcherFactory);
}
}
diff --git a/src/NHibernate/Driver/OracleDataClientDriverBase.cs b/src/NHibernate/Driver/OracleDataClientDriverBase.cs
index 52324e87c07..3ba5f59ce96 100644
--- a/src/NHibernate/Driver/OracleDataClientDriverBase.cs
+++ b/src/NHibernate/Driver/OracleDataClientDriverBase.cs
@@ -2,8 +2,6 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
-using System.Threading;
-using System.Threading.Tasks;
using NHibernate.AdoNet;
using NHibernate.Engine.Query;
using NHibernate.SqlTypes;
@@ -21,24 +19,6 @@ namespace NHibernate.Driver
///
public abstract partial class OracleDataClientDriverBase : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
{
- private partial class OracleDbCommandWrapper : DbCommandWrapper
- {
- private readonly Action