diff --git a/doc/reference/modules/basic_mapping.xml b/doc/reference/modules/basic_mapping.xml
index 839a3880ff5..b2407efade5 100644
--- a/doc/reference/modules/basic_mapping.xml
+++ b/doc/reference/modules/basic_mapping.xml
@@ -2633,7 +2633,7 @@
types, System.Object types, and System.Object types for large objects. Just like
Columns for System.ValueType types can handle null values only if the entity property is properly
typed with a Nullable<T>. Otherwise null will be replaced by the default
- value for the type when reading, and when be overwritten by it when persisting the entity, potentially leading to
+ value for the type when reading, and then will be overwritten by it when persisting the entity, potentially leading to
phantom updates.
@@ -3025,6 +3025,12 @@
NHibernate.Type.TypeFactory.
+
+ Default NHibernate types used when no type attribute is specified can be overridden by using
+ the NHibernate.Type.TypeFactory.RegisterType static method before configuring and building
+ session factories.
+
+
diff --git a/src/NHibernate.Test/TypesTest/ChangeDefaultTypeClass.cs b/src/NHibernate.Test/TypesTest/ChangeDefaultTypeClass.cs
new file mode 100644
index 00000000000..1dca978091e
--- /dev/null
+++ b/src/NHibernate.Test/TypesTest/ChangeDefaultTypeClass.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace NHibernate.Test.TypesTest
+{
+ public class ChangeDefaultTypeClass
+ {
+ public int Id { get; set; }
+
+ public DateTime NormalDateTimeValue { get; set; } = DateTime.Today;
+ }
+}
diff --git a/src/NHibernate.Test/TypesTest/ChangeDefaultTypeClass.hbm.xml b/src/NHibernate.Test/TypesTest/ChangeDefaultTypeClass.hbm.xml
new file mode 100644
index 00000000000..f2cfaf24dec
--- /dev/null
+++ b/src/NHibernate.Test/TypesTest/ChangeDefaultTypeClass.hbm.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate.Test/TypesTest/ChangeDefaultTypeFixture.cs b/src/NHibernate.Test/TypesTest/ChangeDefaultTypeFixture.cs
new file mode 100644
index 00000000000..d54f3876f33
--- /dev/null
+++ b/src/NHibernate.Test/TypesTest/ChangeDefaultTypeFixture.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using NHibernate.Cfg;
+using NHibernate.Engine;
+using NHibernate.Impl;
+using NHibernate.Type;
+using NUnit.Framework;
+
+namespace NHibernate.Test.TypesTest
+{
+ ///
+ /// TestFixtures for changing a default .Net type.
+ ///
+ [TestFixture]
+ public class ChangeDefaultTypeFixture : TypeFixtureBase
+ {
+ protected override string TypeName => "ChangeDefaultType";
+
+ private IType _originalDefaultDateTimeType;
+ private IType _testDefaultDateTimeType;
+
+ protected override void Configure(Configuration configuration)
+ {
+ _originalDefaultDateTimeType = TypeFactory.GetDefaultTypeFor(typeof(DateTime));
+ Assert.That(_originalDefaultDateTimeType, Is.Not.Null);
+ _testDefaultDateTimeType = NHibernateUtil.DateTime.Equals(_originalDefaultDateTimeType)
+ ? (IType) NHibernateUtil.DateTimeNoMs
+ : NHibernateUtil.DateTime;
+ TypeFactory.RegisterType(typeof(DateTime), _testDefaultDateTimeType, TypeFactory.EmptyAliases);
+ base.Configure(configuration);
+ }
+
+ protected override void DropSchema()
+ {
+ base.DropSchema();
+ if (_originalDefaultDateTimeType != null)
+ TypeFactory.RegisterType(typeof(DateTime), _originalDefaultDateTimeType, TypeFactory.EmptyAliases);
+ }
+
+ [Test]
+ public void DefaultType()
+ {
+ Assert.That(TypeFactory.GetDefaultTypeFor(typeof(DateTime)), Is.EqualTo(_testDefaultDateTimeType));
+ }
+
+ [Test]
+ public void PropertyType()
+ {
+ Assert.That(
+ Sfi.GetClassMetadata(typeof(ChangeDefaultTypeClass))
+ .GetPropertyType(nameof(ChangeDefaultTypeClass.NormalDateTimeValue)),
+ Is.EqualTo(_testDefaultDateTimeType));
+ }
+
+ [Test]
+ public void GuessType()
+ {
+ Assert.That(NHibernateUtil.GuessType(typeof(DateTime)), Is.EqualTo(_testDefaultDateTimeType));
+ }
+
+ [Test]
+ public void ParameterType()
+ {
+ var namedParametersField = typeof(AbstractQueryImpl)
+ .GetField("namedParameters", BindingFlags.Instance | BindingFlags.NonPublic);
+ Assert.That(namedParametersField, Is.Not.Null, "Missing internal field");
+
+ using (var s = OpenSession())
+ {
+ // Query where the parameter type cannot be deducted from compared entity property
+ var q = s.CreateQuery($"from {nameof(ChangeDefaultTypeClass)} where :date1 = :date2 or :date1 = :date3")
+ .SetParameter("date1", DateTime.Now)
+ .SetDateTime("date2", DateTime.Now)
+ .SetDateTimeNoMs("date3", DateTime.Now);
+
+ var namedParameters = namedParametersField.GetValue(q) as Dictionary;
+ Assert.That(namedParameters, Is.Not.Null, "Unable to retrieve parameters internal field");
+ Assert.That(namedParameters["date1"].Type, Is.EqualTo(_testDefaultDateTimeType));
+ Assert.That(namedParameters["date2"].Type, Is.EqualTo(NHibernateUtil.DateTime));
+ Assert.That(namedParameters["date3"].Type, Is.EqualTo(NHibernateUtil.DateTimeNoMs));
+ }
+ }
+ }
+}
diff --git a/src/NHibernate/Type/TypeFactory.cs b/src/NHibernate/Type/TypeFactory.cs
index 9d14ce6812f..cfa5e405e47 100644
--- a/src/NHibernate/Type/TypeFactory.cs
+++ b/src/NHibernate/Type/TypeFactory.cs
@@ -6,6 +6,7 @@
using System.Xml;
using System.Xml.Linq;
using NHibernate.Bytecode;
+using NHibernate.Cfg;
using NHibernate.Classic;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;
@@ -33,8 +34,9 @@ private enum TypeClassification
PrecisionScale
}
+ public static readonly string[] EmptyAliases = System.Array.Empty();
+
private static readonly INHibernateLogger _log = NHibernateLogger.For(typeof(TypeFactory));
- private static readonly string[] EmptyAliases= System.Array.Empty();
private static readonly char[] PrecisionScaleSplit = { '(', ')', ',' };
private static readonly char[] LengthSplit = { '(', ')' };
@@ -95,7 +97,15 @@ private enum TypeClassification
private delegate NullableType NullableTypeCreatorDelegate(SqlType sqlType);
- private static void RegisterType(System.Type systemType, IType nhibernateType, IEnumerable aliases)
+ ///
+ /// Defines which NHibernate type should be chosen by default for handling a given .Net type.
+ /// This must be done before any operation on NHibernate, including building its
+ /// and building session factory. Otherwise the behavior will be undefined.
+ ///
+ /// The .Net type.
+ /// The NHibernate type.
+ /// The additional aliases to map to the type. Use if none.
+ public static void RegisterType(System.Type systemType, IType nhibernateType, IEnumerable aliases)
{
var typeAliases = new List(aliases);
typeAliases.AddRange(GetClrTypeAliases(systemType));