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