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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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();
+	}
+
+	/// <summary>
+	/// 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.
+	/// </summary>
+	/// <param name="name"></param>
+	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<T, U>(CultureInfo from, CultureInfo to, T expectedValue, Action<T, T> assert, CancellationToken cancellationToken = default(CancellationToken))
+		where T : struct
+		where U : DataEntity<T>, 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<U>(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<DateTime, NHDateTimeEntity>(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<DateTime, DateTimeEntity>(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<decimal, DecimalEntity>(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<double, DoubleEntity>(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<int, IntegerEntity>(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<float, FloatEntity>(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<T>:Entity where T : struct
+{
+	public virtual T DataValue { get; set; }
+}
+
+public class IntegerEntity : DataEntity<int> { }
+public class DateTimeEntity : DataEntity<DateTime> { }
+
+public class DoubleEntity : DataEntity<double> { }
+public class DecimalEntity : DataEntity<decimal> { }
+public class FloatEntity : DataEntity<float> { }
+public class  NHDateTimeEntity : DataEntity<DateTime> { }
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();
+	}
+
+	/// <summary>
+	/// 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.
+	/// </summary>
+	/// <param name="name"></param>
+	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<T, U>(CultureInfo from, CultureInfo to, T expectedValue, Action<T, T> assert)
+		where T : struct
+		where U : DataEntity<T>, 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<U>(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<DateTime, NHDateTimeEntity>(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<DateTime, DateTimeEntity>(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<decimal, DecimalEntity>(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<double, DoubleEntity>(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<int, IntegerEntity>(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<float, FloatEntity>(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 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
+                   namespace="NHibernate.Test.NHSpecificTest.GH3530">
+  <!--
+  The following classes have custom-built schema to override the
+  database column type to a text type as the SchemaExporter does
+  not provide a mechanism of doing this, hence they have a
+  schema-action of 'none'.
+  -->
+  <class name="IntegerEntity" schema-action="none">
+    <id name="Id" generator="guid.comb" />
+    <property name="DataValue" column="DataValue"/>
+  </class>
+  <class name="DateTimeEntity" schema-action="none">
+    <id name="Id" generator="guid.comb" />
+    <property name="DataValue" column="DataValue"/>
+  </class>
+  <class name="DoubleEntity" schema-action="none">
+    <id name="Id" generator="guid.comb" />
+    <property name="DataValue" column="DataValue"/>
+  </class>
+  <class name="DecimalEntity" schema-action="none">
+    <id name="Id" generator="guid.comb" />
+    <property name="DataValue" column="DataValue"/>
+  </class>
+  <class name="FloatEntity" schema-action="none">
+    <id name="Id" generator="guid.comb"/>
+    <property name="DataValue" column="DataValue"/>
+  </class>
+  
+  <!--
+  The following classes will be exported as expected by the SchemaExport.
+  -->
+  <class name="NHDateTimeEntity">
+    <id name="Id" generator="guid.comb" />
+    <property name="DataValue" column="DataValue"/>
+  </class>
+</hibernate-mapping>
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<ISessionImplementor>();
 			var reader = Substitute.For<DbDataReader>();
+
+			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<ISessionImplementor>();
 			var reader = Substitute.For<DbDataReader>();
+
 			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<ISessionImplementor>();
 			var reader = Substitute.For<DbDataReader>();
+
+			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
+{
+	/// <summary>
+	/// 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.
+	/// </summary>
+	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
+{
+	/// <summary>
+	/// 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 <see cref="IFormatProvider"/>.
+	/// </summary>
+	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<DbDataReader> 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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<bool> NextResultAsync(CancellationToken cancellationToken)
+		{
+			if (cancellationToken.IsCancellationRequested)
+			{
+				return Task.FromCanceled<bool>(cancellationToken);
+			}
+			return DataReader.NextResultAsync(cancellationToken);
+		}
+
+		public override Task<bool> ReadAsync(CancellationToken cancellationToken)
+		{
+			if (cancellationToken.IsCancellationRequested)
+			{
+				return Task.FromCanceled<bool>(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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<bool> NextResultAsync(CancellationToken cancellationToken)
+		{
+			if (cancellationToken.IsCancellationRequested)
+			{
+				return Task.FromCanceled<bool>(cancellationToken);
+			}
+			return DataReader.NextResultAsync(cancellationToken);
+		}
+
+		public override Task<bool> ReadAsync(CancellationToken cancellationToken)
+		{
+			if (cancellationToken.IsCancellationRequested)
+			{
+				return Task.FromCanceled<bool>(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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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
+
+		/// <inheritdoc />
+		public virtual Task<DbDataReader> ExecuteReaderAsync(DbCommand command, CancellationToken cancellationToken)
+		{
+			if (cancellationToken.IsCancellationRequested)
+			{
+				return Task.FromCanceled<DbDataReader>(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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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
+		/// <summary>
+		/// Executes the command and returns a <see cref="DbDataReader"/>.
+		/// </summary>
+		/// <param name="driver">The driver.</param>
+		/// <param name="command">The command to execute.</param>
+		/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
+		/// <returns>A DbDataReader</returns>
+		public static Task<DbDataReader> ExecuteReaderAsync(this IDriver driver, DbCommand command, CancellationToken cancellationToken)
+		{
+			if (cancellationToken.IsCancellationRequested)
+			{
+				return Task.FromCanceled<DbDataReader>(cancellationToken);
+			}
+			try
+			{
+				return driver is DriverBase driverBase ? driverBase.ExecuteReaderAsync(command, cancellationToken) : command.ExecuteReaderAsync(cancellationToken);
+			}
+			catch (System.Exception ex)
+			{
+				return Task.FromException<DbDataReader>(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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<DbDataReader> 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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<DbDataReader> 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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<DbDataReader> 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<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
+		public override Task<DbDataReader> 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<DbDataReader>(cancellationToken);
+			}
+			return InternalExecuteReaderAsync();
+			async Task<DbDataReader> 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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<DbDataReader> 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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<DbDataReader> 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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<DbDataReader> 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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<DbDataReader> 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 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by AsyncGenerator.
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+
+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<DbDataReader> 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
 		/// <summary> A default database catalog name to use for unqualified tablenames</summary>
 		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<string, string> 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
 	/// <summary>
 	/// Base class for the implementation of IDriver
 	/// </summary>
-	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.
 		/// </summary>
 		public virtual int CommandTimeout => commandTimeout;
+
+		/// <inheritdoc />
+		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
+		/// <summary>
+		/// Executes the command and returns a <see cref="DbDataReader"/>.
+		/// </summary>
+		/// <param name="driver">The driver.</param>
+		/// <param name="command">The command to execute.</param>
+		/// <returns>A DbDataReader</returns>
+		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
 	/// <c>FirebirdSql.Data.FirebirdClient</c> assembly.
 	/// </summary>
-	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.
 		/// </summary>
 		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
 	/// <summary>
 	///     A NHibernate Driver for using the SqlClient DataProvider
 	/// </summary>
-	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.
 	/// </para>
 	/// </remarks>
-	public class MySqlDataDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
+	public partial class MySqlDataDriver : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
 	{
 		/// <summary>
 		/// Initializes a new instance of the <see cref="MySqlDataDriver"/> class.
@@ -79,6 +80,13 @@ public override IResultSetsCommand GetResultSetsCommand(Engine.ISessionImplement
 		/// <inheritdoc />
 		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
 	/// </remarks>
 	public abstract partial class OracleDataClientDriverBase : ReflectionBasedDriver, IEmbeddedBatcherFactoryProvider
 	{
-		private partial class OracleDbCommandWrapper : DbCommandWrapper
-		{
-			private readonly Action<object, bool> _suppressDecimalInvalidCastExceptionSetter;
-
-			public OracleDbCommandWrapper(DbCommand command, Action<object, bool> suppressDecimalInvalidCastExceptionSetter) : base(command)
-			{
-				_suppressDecimalInvalidCastExceptionSetter = suppressDecimalInvalidCastExceptionSetter;
-			}
-
-			protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
-			{
-				var reader = Command.ExecuteReader(behavior);
-				_suppressDecimalInvalidCastExceptionSetter(reader, true);
-
-				return reader;
-			}
-		}
-
 		private const string _commandClassName = "OracleCommand";
 
 		private static readonly SqlType _guidSqlType = new SqlType(DbType.Binary, 16);
@@ -54,6 +34,8 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
 		private readonly object _oracleDbTypeBinaryDouble;
 		private readonly object _oracleDbTypeBinaryFloat;
 
+		private readonly Func<object, object> _oracleGetSessionInfo;
+		private readonly Func<object, string> _oracleGetTimeStampFormat;
 		/// <summary>
 		/// Default constructor.
 		/// </summary>
@@ -89,6 +71,10 @@ private OracleDataClientDriverBase(string driverAssemblyName, string clientNames
 			{
 				_suppressDecimalInvalidCastExceptionSetter = DelegateHelper.BuildPropertySetter<bool>(oracleDataReader, "SuppressGetDecimalInvalidCastException");
 			}
+
+			var oracleGlobalization = ReflectHelper.TypeFromAssembly(clientNamespace + ".OracleGlobalization", driverAssemblyName, true);
+			_oracleGetTimeStampFormat = DelegateHelper.BuildPropertyGetter<string>(oracleGlobalization, "TimeStampFormat");
+			_oracleGetSessionInfo = DelegateHelper.BuildFunc<object>(TypeOfConnection, "GetSessionInfo");
 		}
 
 		/// <inheritdoc/>
@@ -221,25 +207,35 @@ protected override void OnBeforePrepare(DbCommand command)
 			command.Parameters.Insert(0, outCursor);
 		}
 
-		public override DbCommand CreateCommand()
+		public override DbDataReader ExecuteReader(DbCommand command)
 		{
-			var command = base.CreateCommand();
-			if (!SuppressDecimalInvalidCastException)
+			if (!SuppressDecimalInvalidCastException && _suppressDecimalInvalidCastExceptionSetter == null)
 			{
-				return command;
+				throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer");
 			}
 
-			if (_suppressDecimalInvalidCastExceptionSetter == null)
+			var reader = command.ExecuteReader();
+
+			if (SuppressDecimalInvalidCastException)
 			{
-				throw new NotSupportedException("OracleDataReader.SuppressGetDecimalInvalidCastException property is supported only in ODP.NET version 19.10 or newer");
+				_suppressDecimalInvalidCastExceptionSetter(reader, true);
 			}
 
-			return new OracleDbCommandWrapper(command, _suppressDecimalInvalidCastExceptionSetter);
+			string timestampFormat = GetDateFormat(command.Connection);
+
+			return new OracleDbDataReader(reader, timestampFormat);
 		}
 
-		public override DbCommand UnwrapDbCommand(DbCommand command)
+		private string GetDateFormat(DbConnection connection)
 		{
-			return command is OracleDbCommandWrapper wrapper ? wrapper.Command : command;
+			if (_oracleGetSessionInfo == null && _oracleGetTimeStampFormat == null)
+			{
+				return null;
+			}
+
+			var sessionInfo = _oracleGetSessionInfo(connection);
+
+			return _oracleGetTimeStampFormat(sessionInfo);
 		}
 
 		System.Type IEmbeddedBatcherFactoryProvider.BatcherFactoryClass => typeof(OracleDataClientBatchingBatcherFactory);
diff --git a/src/NHibernate/Driver/ReflectionBasedDriver.cs b/src/NHibernate/Driver/ReflectionBasedDriver.cs
index b8108102826..21d89b000c0 100644
--- a/src/NHibernate/Driver/ReflectionBasedDriver.cs
+++ b/src/NHibernate/Driver/ReflectionBasedDriver.cs
@@ -41,10 +41,10 @@ protected ReflectionBasedDriver(string driverAssemblyName, string connectionType
 		protected ReflectionBasedDriver(string providerInvariantName, string driverAssemblyName, string connectionTypeName, string commandTypeName)
 		{
 			// Try to get the types from an already loaded assembly
-			var connectionType = ReflectHelper.TypeFromAssembly(connectionTypeName, driverAssemblyName, false);
-			var commandType = ReflectHelper.TypeFromAssembly(commandTypeName, driverAssemblyName, false);
+			TypeOfConnection = ReflectHelper.TypeFromAssembly(connectionTypeName, driverAssemblyName, false);
+			TypeOfCommand = ReflectHelper.TypeFromAssembly(commandTypeName, driverAssemblyName, false);
 
-			if (connectionType == null || commandType == null)
+			if (TypeOfConnection == null || TypeOfCommand == null)
 			{
 #if NETFX || NETSTANDARD2_1_OR_GREATER
 				if (string.IsNullOrEmpty(providerInvariantName))
@@ -59,8 +59,8 @@ protected ReflectionBasedDriver(string providerInvariantName, string driverAssem
 			}
 			else
 			{
-				connectionCommandProvider = new ReflectionDriveConnectionCommandProvider(connectionType, commandType);
-				DriverVersion = connectionType.Assembly.GetName().Version;
+				connectionCommandProvider = new ReflectionDriveConnectionCommandProvider(TypeOfConnection, TypeOfCommand);
+				DriverVersion = TypeOfConnection.Assembly.GetName().Version;
 			}
 		}
 
@@ -73,5 +73,8 @@ public override DbCommand CreateCommand()
 		{
 			return connectionCommandProvider.CreateCommand();
 		}
+
+		protected System.Type TypeOfConnection { get; private set; }
+		protected System.Type TypeOfCommand { get; private set; }
 	}
 }
diff --git a/src/NHibernate/Driver/SapSQLAnywhere17Driver.cs b/src/NHibernate/Driver/SapSQLAnywhere17Driver.cs
index cac8b8bed14..392b5c1445c 100644
--- a/src/NHibernate/Driver/SapSQLAnywhere17Driver.cs
+++ b/src/NHibernate/Driver/SapSQLAnywhere17Driver.cs
@@ -1,6 +1,9 @@
-namespace NHibernate.Driver
+using System.Data.Common;
+using NHibernate.AdoNet;
+
+namespace NHibernate.Driver
 {
-	public class SapSQLAnywhere17Driver : ReflectionBasedDriver
+	public partial class SapSQLAnywhere17Driver : ReflectionBasedDriver
 	{
 		public SapSQLAnywhere17Driver()
 			: base("Sap.Data.SQLAnywhere", "Sap.Data.SQLAnywhere.v4.5", "Sap.Data.SQLAnywhere.SAConnection", "Sap.Data.SQLAnywhere.SACommand")
@@ -14,5 +17,12 @@ public SapSQLAnywhere17Driver()
 		public override string NamedPrefix => ":";
 
 		public override bool RequiresTimeSpanForTime => true;
+
+		public override DbDataReader ExecuteReader(DbCommand command)
+		{
+			var reader = command.ExecuteReader();
+
+			return new SqlAnywhereDbDataReader(reader);
+		}
 	}
 }
diff --git a/src/NHibernate/Driver/Sql2008ClientDriver.cs b/src/NHibernate/Driver/Sql2008ClientDriver.cs
index 0068832139f..6255f12ffef 100644
--- a/src/NHibernate/Driver/Sql2008ClientDriver.cs
+++ b/src/NHibernate/Driver/Sql2008ClientDriver.cs
@@ -2,10 +2,11 @@
 using System.Data;
 using System.Data.Common;
 using NHibernate.Util;
+using NHibernate.AdoNet;
 
 namespace NHibernate.Driver
 {
-	public class Sql2008ClientDriver : SqlClientDriver
+	public partial class Sql2008ClientDriver : SqlClientDriver
 	{
 		const byte MaxTime = 5;
 
@@ -34,5 +35,12 @@ protected override void InitializeParameter(DbParameter dbParam, string name, Sq
 
 		/// <inheritdoc />
 		public override DateTime MinDate => DateTime.MinValue;
+
+		public override DbDataReader ExecuteReader(DbCommand command)
+		{
+			var reader = command.ExecuteReader();
+
+			return new NoCharDbDataReader(reader);
+		}
 	}
 }
diff --git a/src/NHibernate/Driver/SqlServerCeDriver.cs b/src/NHibernate/Driver/SqlServerCeDriver.cs
index 0b6a4ad93bc..7e8a720d4de 100644
--- a/src/NHibernate/Driver/SqlServerCeDriver.cs
+++ b/src/NHibernate/Driver/SqlServerCeDriver.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Data;
 using System.Data.Common;
+using NHibernate.AdoNet;
 using NHibernate.SqlTypes;
 using NHibernate.Util;
 
@@ -9,7 +10,7 @@ namespace NHibernate.Driver
 	/// <summary>
 	/// A NHibernate driver for Microsoft SQL Server CE data provider
 	/// </summary>
-	public class SqlServerCeDriver : ReflectionBasedDriver
+	public partial class SqlServerCeDriver : ReflectionBasedDriver
 	{
 		private static readonly Action<object, SqlDbType> SetSqlDbType =
 			DelegateHelper.BuildPropertySetter<SqlDbType>(
@@ -125,5 +126,12 @@ private void AdjustDbParamTypeForLargeObjects(DbParameter dbParam, SqlType sqlTy
 
 		/// <inheritdoc />
 		public override DateTime MinDate => new DateTime(1753, 1, 1);
+
+		public override DbDataReader ExecuteReader(DbCommand command)
+		{
+			var reader = command.ExecuteReader();
+
+			return new NoCharDbDataReader(reader);
+		}
 	}
 }
diff --git a/src/NHibernate/Driver/SybaseSQLAnywhereDotNet4Driver.cs b/src/NHibernate/Driver/SybaseSQLAnywhereDotNet4Driver.cs
index a7aae90d27d..702f57c4bf9 100644
--- a/src/NHibernate/Driver/SybaseSQLAnywhereDotNet4Driver.cs
+++ b/src/NHibernate/Driver/SybaseSQLAnywhereDotNet4Driver.cs
@@ -1,4 +1,7 @@
-namespace NHibernate.Driver
+using System.Data.Common;
+using NHibernate.AdoNet;
+
+namespace NHibernate.Driver
 {
 	/// <summary>
 	/// SQL Dialect for SQL Anywhere 12 - for the NHibernate 3.2.0 distribution
@@ -28,7 +31,7 @@
 	/// Sybase SQL Anywhere 12 using the versioned ADO.NET driver 
 	/// iAnywhere.Data.SQLAnywhere.v4.0.
 	/// </remarks>
-	public class SybaseSQLAnywhereDotNet4Driver : ReflectionBasedDriver
+	public partial class SybaseSQLAnywhereDotNet4Driver : ReflectionBasedDriver
 	{
 		/// <summary>
 		/// Initializes a new instance of the <see cref="SybaseSQLAnywhereDotNet4Driver"/> class.
@@ -57,5 +60,12 @@ public override string NamedPrefix
 		}
 
 		public override bool RequiresTimeSpanForTime => true;
+
+		public override DbDataReader ExecuteReader(DbCommand command)
+		{
+			var reader = command.ExecuteReader();
+
+			return new SqlAnywhereDbDataReader(reader);
+		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Driver/SybaseSQLAnywhereDriver.cs b/src/NHibernate/Driver/SybaseSQLAnywhereDriver.cs
index 94fd2ee5111..571391512c8 100644
--- a/src/NHibernate/Driver/SybaseSQLAnywhereDriver.cs
+++ b/src/NHibernate/Driver/SybaseSQLAnywhereDriver.cs
@@ -1,9 +1,12 @@
-namespace NHibernate.Driver
+using System.Data.Common;
+using NHibernate.AdoNet;
+
+namespace NHibernate.Driver
 {
 	/// <summary>
 	/// The SybaseSQLAnywhereDriver Driver provides a database driver for Sybase SQL Anywhere 10 and above
 	/// </summary>
-	public class SybaseSQLAnywhereDriver : ReflectionBasedDriver
+	public partial class SybaseSQLAnywhereDriver : ReflectionBasedDriver
 	{
 		/// <summary>
 		/// Initializes a new instance of the <see cref="SybaseSQLAnywhereDriver"/> class.
@@ -32,5 +35,12 @@ public override string NamedPrefix
 		}
 
 		public override bool RequiresTimeSpanForTime => true;
+
+		public override DbDataReader ExecuteReader(DbCommand command)
+		{
+			var reader = command.ExecuteReader();
+
+			return new SqlAnywhereDbDataReader(reader);
+		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Type/AbstractCharType.cs b/src/NHibernate/Type/AbstractCharType.cs
index ba2ad5bb761..879640bf3be 100644
--- a/src/NHibernate/Type/AbstractCharType.cs
+++ b/src/NHibernate/Type/AbstractCharType.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Data.Common;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -20,12 +21,24 @@ public AbstractCharType(SqlType sqlType) : base(sqlType)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			string dbValue = Convert.ToString(rs[index]);
+			if (rs.TryGetChar(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
+			if (!rs.TryGetString(index, out var strValue))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				strValue = Convert.ToString(rs[index], locale);
+			}
+
 			// The check of the Length is a workaround see NH-2340
-			if (dbValue.Length > 0)
+			if (strValue.Length > 0)
 			{
-				return dbValue[0];
+				return strValue[0];
 			}
+
 			return '\0'; // This line should never be executed
 		}
 
diff --git a/src/NHibernate/Type/AbstractDateTimeType.cs b/src/NHibernate/Type/AbstractDateTimeType.cs
index 9deb2273d3e..bf57167de0f 100644
--- a/src/NHibernate/Type/AbstractDateTimeType.cs
+++ b/src/NHibernate/Type/AbstractDateTimeType.cs
@@ -3,6 +3,7 @@
 using System.Collections.Generic;
 using System.Data.Common;
 using System.Globalization;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -64,7 +65,14 @@ protected virtual DateTime GetDateTime(DbDataReader rs, int index, ISessionImple
 		{
 			try
 			{
-				return AdjustDateTime(Convert.ToDateTime(rs[index]));
+				if (!rs.TryGetDateTime(index, out var dbValue))
+				{
+					var locale = session.Factory.Settings.Locale;
+
+					dbValue = Convert.ToDateTime(rs[index], locale);
+				}
+
+				return AdjustDateTime(dbValue);
 			}
 			catch (Exception ex)
 			{
diff --git a/src/NHibernate/Type/AbstractStringType.cs b/src/NHibernate/Type/AbstractStringType.cs
index e5c073b7df7..bbecbb24e0d 100644
--- a/src/NHibernate/Type/AbstractStringType.cs
+++ b/src/NHibernate/Type/AbstractStringType.cs
@@ -1,7 +1,8 @@
-using System;
+using System;
 using System.Collections.Generic;
 using System.Data.Common;
 using System.Globalization;
+using NHibernate.AdoNet;
 using NHibernate.Driver;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
@@ -73,7 +74,14 @@ public override void Set(DbCommand cmd, object value, int index, ISessionImpleme
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			return Convert.ToString(rs[index]);
+			if (!rs.TryGetString(index, out var dbValue))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				dbValue = Convert.ToString(rs[index], locale);
+			}
+
+			return dbValue;
 		}
 
 		public override bool IsEqual(object x, object y)
diff --git a/src/NHibernate/Type/BooleanType.cs b/src/NHibernate/Type/BooleanType.cs
index d93e23e0385..423fb0fca68 100644
--- a/src/NHibernate/Type/BooleanType.cs
+++ b/src/NHibernate/Type/BooleanType.cs
@@ -2,6 +2,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Runtime.CompilerServices;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -40,7 +41,14 @@ public BooleanType(AnsiStringFixedLengthSqlType sqlType) : base(sqlType)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			return GetBooleanAsObject(Convert.ToBoolean(rs[index]));
+			if (!rs.TryGetBoolean(index, out var dbValue))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				dbValue = Convert.ToBoolean(rs[index], locale);
+			}
+
+			return GetBooleanAsObject(dbValue);
 		}
 
 		public override System.Type PrimitiveClass => typeof(bool);
diff --git a/src/NHibernate/Type/ByteType.cs b/src/NHibernate/Type/ByteType.cs
index f52071890ff..ba76f2acd3c 100644
--- a/src/NHibernate/Type/ByteType.cs
+++ b/src/NHibernate/Type/ByteType.cs
@@ -3,6 +3,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Numerics;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -24,11 +25,17 @@ public ByteType() : base(SqlTypeFactory.Byte)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			return rs[index] switch
+			if (rs.TryGetByte(index, out var dbValue))
 			{
+				return dbValue;
+			}
+
+			var locale = session.Factory.Settings.Locale;
 
+			return rs[index] switch
+			{
 				BigInteger bi => (byte) bi,
-				var c => Convert.ToByte(c)
+				var c => Convert.ToByte(c, locale)
 			};
 		}
 
diff --git a/src/NHibernate/Type/CharBooleanType.cs b/src/NHibernate/Type/CharBooleanType.cs
index 1224fb363d5..e4db1b41063 100644
--- a/src/NHibernate/Type/CharBooleanType.cs
+++ b/src/NHibernate/Type/CharBooleanType.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Data;
 using System.Data.Common;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -29,14 +30,20 @@ protected CharBooleanType(AnsiStringFixedLengthSqlType sqlType) : base(sqlType)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			string code = Convert.ToString(rs[index]);
-			if (code == null)
+			if (!rs.TryGetString(index, out var dbValue))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				dbValue = Convert.ToString(rs[index], locale);
+			}
+
+			if (dbValue == null)
 			{
 				return null;
 			}
 			else
 			{
-				return GetBooleanAsObject(code.Equals(TrueString, StringComparison.InvariantCultureIgnoreCase));
+				return GetBooleanAsObject(dbValue.Equals(TrueString, StringComparison.InvariantCultureIgnoreCase));
 			}
 		}
 
diff --git a/src/NHibernate/Type/DecimalType.cs b/src/NHibernate/Type/DecimalType.cs
index e2398ec4ce6..3ef69f9a4d0 100644
--- a/src/NHibernate/Type/DecimalType.cs
+++ b/src/NHibernate/Type/DecimalType.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Data;
 using System.Data.Common;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -28,7 +29,14 @@ public DecimalType(SqlType sqlType) : base(sqlType)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			return Convert.ToDecimal(rs[index]);
+			if (!rs.TryGetDecimal(index, out var dbValue))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				dbValue = Convert.ToDecimal(rs[index], locale);
+			}
+
+			return dbValue;
 		}
 
 		public override System.Type ReturnedClass => typeof(Decimal);
diff --git a/src/NHibernate/Type/DoubleType.cs b/src/NHibernate/Type/DoubleType.cs
index 45b5713f525..07cd50c0f09 100644
--- a/src/NHibernate/Type/DoubleType.cs
+++ b/src/NHibernate/Type/DoubleType.cs
@@ -2,6 +2,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Numerics;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -28,10 +29,17 @@ public DoubleType(SqlType sqlType) : base(sqlType)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetDouble(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
+			var locale = session.Factory.Settings.Locale;
+
 			return rs[index] switch
 			{
 				BigInteger bi => (double) bi,
-				var v => Convert.ToDouble(v)
+				var v => Convert.ToDouble(v, locale)
 			};
 		}
 
diff --git a/src/NHibernate/Type/GuidType.cs b/src/NHibernate/Type/GuidType.cs
index 2dc627d5d09..fc96faf9d4b 100644
--- a/src/NHibernate/Type/GuidType.cs
+++ b/src/NHibernate/Type/GuidType.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Data;
 using System.Data.Common;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -22,9 +23,9 @@ public GuidType() : base(SqlTypeFactory.Guid)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			if (rs.GetFieldType(index) == typeof (Guid))
+			if (rs.TryGetGuid(index, out var dbValue))
 			{
-				return rs.GetGuid(index);
+				return dbValue;
 			}
 
 			if (rs.GetFieldType(index) == typeof(byte[]))
@@ -32,7 +33,14 @@ public override object Get(DbDataReader rs, int index, ISessionImplementor sessi
 				return new Guid((byte[])(rs[index]));
 			} 
 
-			return new Guid(Convert.ToString(rs[index]));
+			if (!rs.TryGetString(index, out var dbString))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				dbString = Convert.ToString(rs[index], locale);
+			}
+
+			return new Guid(dbString);
 		}
 
 		/// <summary></summary>
diff --git a/src/NHibernate/Type/Int16Type.cs b/src/NHibernate/Type/Int16Type.cs
index cfd21908465..b090fe153bc 100644
--- a/src/NHibernate/Type/Int16Type.cs
+++ b/src/NHibernate/Type/Int16Type.cs
@@ -6,6 +6,7 @@
 using System.Collections.Generic;
 using System.Data;
 using System.Numerics;
+using NHibernate.AdoNet;
 
 namespace NHibernate.Type
 {
@@ -28,12 +29,19 @@ public Int16Type() : base(SqlTypeFactory.Int16)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetInt16(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
 			try
 			{
+				var locale = session.Factory.Settings.Locale;
+
 				return rs[index] switch
 				{
 					BigInteger bi => (short) bi,
-					var c => Convert.ToInt16(c)
+					var c => Convert.ToInt16(c, locale)
 				};
 			}
 			catch (Exception ex)
diff --git a/src/NHibernate/Type/Int32Type.cs b/src/NHibernate/Type/Int32Type.cs
index f19fb1148fd..3c8cbf7ff21 100644
--- a/src/NHibernate/Type/Int32Type.cs
+++ b/src/NHibernate/Type/Int32Type.cs
@@ -6,6 +6,7 @@
 using System.Collections.Generic;
 using System.Data;
 using System.Numerics;
+using NHibernate.AdoNet;
 
 namespace NHibernate.Type
 {
@@ -28,12 +29,19 @@ public Int32Type() : base(SqlTypeFactory.Int32)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetInt32(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
 			try
 			{
+				var locale = session.Factory.Settings.Locale;
+
 				return rs[index] switch
 				{
 					BigInteger bi => (int) bi,
-					var c => Convert.ToInt32(c)
+					var c => Convert.ToInt32(c, locale)
 				};
 			}
 			catch (Exception ex)
diff --git a/src/NHibernate/Type/Int64Type.cs b/src/NHibernate/Type/Int64Type.cs
index b86cb619443..4e2b3deb6b0 100644
--- a/src/NHibernate/Type/Int64Type.cs
+++ b/src/NHibernate/Type/Int64Type.cs
@@ -4,6 +4,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Numerics;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -28,12 +29,19 @@ public Int64Type() : base(SqlTypeFactory.Int64)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetInt64(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
 			try
 			{
+				var locale = session.Factory.Settings.Locale;
+
 				return rs[index] switch
 				{
 					BigInteger bi => (long) bi,
-					var c => Convert.ToInt64(c)
+					var c => Convert.ToInt64(c, locale)
 				};
 			}
 			catch (Exception ex)
diff --git a/src/NHibernate/Type/SByteType.cs b/src/NHibernate/Type/SByteType.cs
index 513cacb6e70..817c714a316 100644
--- a/src/NHibernate/Type/SByteType.cs
+++ b/src/NHibernate/Type/SByteType.cs
@@ -4,6 +4,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Numerics;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -28,12 +29,19 @@ public SByteType() : base(SqlTypeFactory.SByte)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetSByte(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
 			try
 			{
+				var locale = session.Factory.Settings.Locale;
+
 				return rs[index] switch
 				{
 					BigInteger bi => (sbyte) bi,
-					var c => Convert.ToSByte(c)
+					var c => Convert.ToSByte(c, locale)
 				};
 			}
 			catch (Exception ex)
diff --git a/src/NHibernate/Type/SingleType.cs b/src/NHibernate/Type/SingleType.cs
index a5d56ce9ade..d5fffec0d84 100644
--- a/src/NHibernate/Type/SingleType.cs
+++ b/src/NHibernate/Type/SingleType.cs
@@ -2,6 +2,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Numerics;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -34,12 +35,19 @@ public SingleType(SqlType sqlType) : base(sqlType)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetFloat(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
 			try
 			{
+				var locale = session.Factory.Settings.Locale;
+
 				return rs[index] switch
 				{
 					BigInteger bi => (float) bi,
-					var v => Convert.ToSingle(v)
+					var v => Convert.ToSingle(v, locale)
 				};
 			}
 			catch (Exception ex)
diff --git a/src/NHibernate/Type/TicksType.cs b/src/NHibernate/Type/TicksType.cs
index 4fc18a007cd..f21d534365b 100644
--- a/src/NHibernate/Type/TicksType.cs
+++ b/src/NHibernate/Type/TicksType.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Data;
 using System.Data.Common;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
diff --git a/src/NHibernate/Type/TimeAsTimeSpanType.cs b/src/NHibernate/Type/TimeAsTimeSpanType.cs
index f5218c0775c..2d47923e26f 100644
--- a/src/NHibernate/Type/TimeAsTimeSpanType.cs
+++ b/src/NHibernate/Type/TimeAsTimeSpanType.cs
@@ -1,10 +1,11 @@
-using System;
+using System;
 using System.Collections;
 using System.Data.Common;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 using System.Collections.Generic;
 using System.Data;
+using NHibernate.AdoNet;
 
 namespace NHibernate.Type
 {
@@ -35,14 +36,19 @@ public override object Get(DbDataReader rs, int index, ISessionImplementor sessi
 		{
 			try
 			{
-				var value = rs[index];
-				if (value is TimeSpan time) //For those dialects where DbType.Time means TimeSpan.
-					return time;
-
-				// Todo: investigate if this convert should be made culture invariant, here and in other NHibernate types,
-				// such as AbstractDateTimeType and TimeType, or even in all other places doing such converts in NHibernate.
-				var dbValue = Convert.ToDateTime(value);
-				return dbValue.TimeOfDay;
+				if (rs.TryGetTimeSpan(index, out var dbTimeSpan))
+				{
+					return dbTimeSpan;
+				}
+
+				if (!rs.TryGetDateTime(index, out var dbDateTime))
+				{
+					var locale = session.Factory.Settings.Locale;
+
+					dbDateTime = Convert.ToDateTime(rs[index], locale);
+				}
+
+				return dbDateTime.TimeOfDay;
 			}
 			catch (Exception ex)
 			{
diff --git a/src/NHibernate/Type/TimeSpanType.cs b/src/NHibernate/Type/TimeSpanType.cs
index ed37b4629d2..08d80399b18 100644
--- a/src/NHibernate/Type/TimeSpanType.cs
+++ b/src/NHibernate/Type/TimeSpanType.cs
@@ -5,6 +5,7 @@
 using NHibernate.SqlTypes;
 using System.Collections.Generic;
 using System.Data;
+using NHibernate.AdoNet;
 
 namespace NHibernate.Type
 {
@@ -27,7 +28,14 @@ public override object Get(DbDataReader rs, int index, ISessionImplementor sessi
 		{
 			try
 			{
-				return new TimeSpan(Convert.ToInt64(rs[index]));
+				if (!rs.TryGetInt64(index, out var dbValue))
+				{
+					var locale = session.Factory.Settings.Locale;
+
+					dbValue = Convert.ToInt64(rs[index], locale);
+				}
+
+				return new TimeSpan(dbValue);
 			}
 			catch (Exception ex)
 			{
diff --git a/src/NHibernate/Type/TimeType.cs b/src/NHibernate/Type/TimeType.cs
index c095ef0c0d2..14a53e97c08 100644
--- a/src/NHibernate/Type/TimeType.cs
+++ b/src/NHibernate/Type/TimeType.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Data;
 using System.Data.Common;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -50,20 +51,26 @@ public override string Name
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			try
+			if (rs.TryGetTimeSpan(index, out var dbTimeSpan))
 			{
-				if (rs[index] is TimeSpan time) //For those dialects where DbType.Time means TimeSpan.
-				{
-					return BaseDateValue.AddTicks(time.Ticks);
-				}
-
-				DateTime dbValue = Convert.ToDateTime(rs[index]);
-				return BaseDateValue.Add(dbValue.TimeOfDay);
+				return BaseDateValue.AddTicks(dbTimeSpan.Ticks);
 			}
-			catch (Exception ex)
+
+			if (!rs.TryGetDateTime(index, out var dbDateTime))
 			{
-				throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex);
+				try
+				{
+					var locale = session.Factory.Settings.Locale;
+
+					dbDateTime = Convert.ToDateTime(rs[index], locale);
+				}
+				catch (Exception ex)
+				{
+					throw new FormatException(string.Format("Input string '{0}' was not in the correct format.", rs[index]), ex);
+				}
 			}
+
+			return BaseDateValue.Add(dbDateTime.TimeOfDay);
 		}
 
 		public override System.Type ReturnedClass
diff --git a/src/NHibernate/Type/UInt16Type.cs b/src/NHibernate/Type/UInt16Type.cs
index c37bbfaca75..398ad3818b9 100644
--- a/src/NHibernate/Type/UInt16Type.cs
+++ b/src/NHibernate/Type/UInt16Type.cs
@@ -4,6 +4,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Numerics;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -28,12 +29,19 @@ public UInt16Type() : base(SqlTypeFactory.UInt16)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetUInt16(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
 			try
 			{
+				var locale = session.Factory.Settings.Locale;
+
 				return rs[index] switch
 				{
 					BigInteger bi => (ushort) bi,
-					var c => Convert.ToUInt16(c)
+					var c => Convert.ToUInt16(c, locale)
 				};
 			}
 			catch (Exception ex)
diff --git a/src/NHibernate/Type/UInt32Type.cs b/src/NHibernate/Type/UInt32Type.cs
index e0e30b65f2f..6f95f96942e 100644
--- a/src/NHibernate/Type/UInt32Type.cs
+++ b/src/NHibernate/Type/UInt32Type.cs
@@ -4,6 +4,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Numerics;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -28,12 +29,19 @@ public UInt32Type() : base(SqlTypeFactory.UInt32)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetUInt32(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
 			try
 			{
+				var locale = session.Factory.Settings.Locale;
+
 				return rs[index] switch
 				{
 					BigInteger bi => (uint) bi,
-					var c => Convert.ToUInt32(c)
+					var c => Convert.ToUInt32(c, locale)
 				};
 			}
 			catch (Exception ex)
diff --git a/src/NHibernate/Type/UInt64Type.cs b/src/NHibernate/Type/UInt64Type.cs
index 8d0c05d20b1..abc4d86dfa2 100644
--- a/src/NHibernate/Type/UInt64Type.cs
+++ b/src/NHibernate/Type/UInt64Type.cs
@@ -4,6 +4,7 @@
 using System.Data;
 using System.Data.Common;
 using System.Numerics;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -27,12 +28,19 @@ public UInt64Type() : base(SqlTypeFactory.UInt64)
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
+			if (rs.TryGetUInt64(index, out var dbValue))
+			{
+				return dbValue;
+			}
+
 			try
 			{
+				var locale = session.Factory.Settings.Locale;
+
 				return rs[index] switch
 				{
 					BigInteger bi => (ulong)bi,
-					var c => Convert.ToUInt64(c)
+					var c => Convert.ToUInt64(c, locale)
 				};
 			}
 			catch (Exception ex)
diff --git a/src/NHibernate/Type/UriType.cs b/src/NHibernate/Type/UriType.cs
index 48fbe91a9c8..f1b17527019 100644
--- a/src/NHibernate/Type/UriType.cs
+++ b/src/NHibernate/Type/UriType.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Data.Common;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -51,7 +52,14 @@ public override void Set(DbCommand cmd, object value, int index, ISessionImpleme
 
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			return StringToObject(Convert.ToString(rs[index]));
+			if (!rs.TryGetString(index, out var dbValue))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				dbValue = Convert.ToString(rs[index], locale);
+			}
+
+			return StringToObject(dbValue);
 		}
 
 		/// <inheritdoc />
diff --git a/src/NHibernate/Type/XDocType.cs b/src/NHibernate/Type/XDocType.cs
index 69912c8ec77..197123d76b8 100644
--- a/src/NHibernate/Type/XDocType.cs
+++ b/src/NHibernate/Type/XDocType.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Data.Common;
 using System.Xml.Linq;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -38,12 +39,18 @@ public override void Set(DbCommand cmd, object value, int index, ISessionImpleme
 		/// <inheritdoc />
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			// according to documentation, GetValue should return a string, at least for MsSQL
-			// hopefully all DataProvider has the same behaviour
-			string xmlString = Convert.ToString(rs.GetValue(index));
+			if (!rs.TryGetString(index, out var dbValue))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				// according to documentation, GetValue should return a string, at least for MsSQL
+				// hopefully all DataProvider has the same behaviour
+				dbValue = Convert.ToString(rs.GetValue(index), locale);
+			}
+
 			// 6.0 TODO: inline the call.
 #pragma warning disable 618
-			return FromStringValue(xmlString);
+			return FromStringValue(dbValue);
 #pragma warning restore 618
 		}
 
diff --git a/src/NHibernate/Type/XmlDocType.cs b/src/NHibernate/Type/XmlDocType.cs
index d4b26317f29..f66bd08b2c8 100644
--- a/src/NHibernate/Type/XmlDocType.cs
+++ b/src/NHibernate/Type/XmlDocType.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Data.Common;
 using System.Xml;
+using NHibernate.AdoNet;
 using NHibernate.Engine;
 using NHibernate.SqlTypes;
 
@@ -37,12 +38,18 @@ public override void Set(DbCommand cmd, object value, int index, ISessionImpleme
 		/// <inheritdoc />
 		public override object Get(DbDataReader rs, int index, ISessionImplementor session)
 		{
-			// according to documentation, GetValue should return a string, at least for MsSQL
-			// hopefully all DataProvider has the same behaviour
-			string xmlString = Convert.ToString(rs.GetValue(index));
+			if (!rs.TryGetString(index, out var dbValue))
+			{
+				var locale = session.Factory.Settings.Locale;
+
+				// according to documentation, GetValue should return a string, at least for MsSQL
+				// hopefully all DataProvider has the same behaviour
+				dbValue = Convert.ToString(rs.GetValue(index), locale);
+			}
+
 			// 6.0 TODO: inline the call.
 #pragma warning disable 618
-			return FromStringValue(xmlString);
+			return FromStringValue(dbValue);
 #pragma warning restore 618
 		}