Skip to content

Commit fff0d08

Browse files
bahusoidhazzik
authored andcommitted
Avoid some cases of Type -> string -> Type conversion in Mapping By Code (nhibernate#2003)
Co-authored-by: Alexander Zaytsev <[email protected]>
1 parent df2ca71 commit fff0d08

File tree

7 files changed

+108
-71
lines changed

7 files changed

+108
-71
lines changed

src/NHibernate.Test/TypesTest/TimestampTypeFixture.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ public void ObsoleteMessage()
2424
var log = spy.GetWholeLog();
2525
Assert.That(
2626
log,
27-
Does.Contain("NHibernate.Type.TimestampType is obsolete. Please use DateTimeType instead.").IgnoreCase);
28-
Assert.That(
29-
log,
30-
Does.Not.Contain($"{NHibernateUtil.Timestamp.Name} is obsolete. Please use DateTimeType instead.").IgnoreCase);
27+
Does.Contain($"NHibernate.Type.TimestampType ({NHibernateUtil.Timestamp.Name}) is obsolete. Please use DateTimeType instead.").IgnoreCase);
3128
}
3229
}
3330
}

src/NHibernate.Test/TypesTest/TypeFactoryFixture.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public void GetNullableGeneric()
5656
//Assert.AreEqual(int64Type, TypeFactory.HeuristicType("Int64?"), "'Int64?' should return a NH Int64Type");
5757

5858
System.Type reflectedType = Util.ReflectHelper.ReflectedPropertyClass( typeof(GenericPropertyClass), "GenericInt64", "property" );
59+
Assert.AreEqual( int64Type, TypeFactory.HeuristicType( reflectedType ), "using System.Type should return nh Int64Type" );
5960
Assert.AreEqual( int64Type, TypeFactory.HeuristicType( reflectedType.AssemblyQualifiedName ), "using AQN should return nh Int64Type" );
6061
Assert.AreEqual( int64Type, TypeFactory.HeuristicType( reflectedType.FullName ), "using FullName should return nh Int64Type" );
6162

@@ -143,5 +144,19 @@ public void WhenUseNullableEnumThenReturnGenericEnumType()
143144
var iType = TypeFactory.HeuristicType(typeof(MyEnum?).AssemblyQualifiedName);
144145
Assert.That(iType, Is.TypeOf<EnumType<MyEnum>>());
145146
}
147+
148+
[Test]
149+
public void WhenUseEnumTypeThenReturnGenericEnumType()
150+
{
151+
var iType = TypeFactory.HeuristicType(typeof (MyEnum));
152+
Assert.That(iType, Is.TypeOf<EnumType<MyEnum>>());
153+
}
154+
155+
[Test]
156+
public void WhenUseNullableEnumTypeThenReturnGenericEnumType()
157+
{
158+
var iType = TypeFactory.HeuristicType(typeof(MyEnum?));
159+
Assert.That(iType, Is.TypeOf<EnumType<MyEnum>>());
160+
}
146161
}
147162
}

src/NHibernate/Hql/Ast/ANTLR/Tree/JavaConstantNode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private void ProcessText()
7070
}
7171

7272
_constantValue = ReflectHelper.GetConstantValue(base.Text, _factory);
73-
_heuristicType = TypeFactory.HeuristicType(_constantValue.GetType().AssemblyQualifiedName);
73+
_heuristicType = TypeFactory.HeuristicType(_constantValue.GetType());
7474
_processedText = true;
7575
}
7676
}

src/NHibernate/Impl/AbstractQueryImpl.cs

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -196,32 +196,22 @@ private IType GuessType(System.Type clazz)
196196
throw new ArgumentNullException("clazz", "The IType can not be guessed for a null value.");
197197
}
198198

199-
string typename = clazz.AssemblyQualifiedName;
200-
IType type = TypeFactory.HeuristicType(typename);
201-
bool serializable = (type != null && type is SerializableType);
202-
if (type == null || serializable)
199+
var type = TypeFactory.HeuristicType(clazz);
200+
if (type == null || type is SerializableType)
203201
{
204-
try
202+
if (session.Factory.TryGetEntityPersister(clazz.FullName) != null)
205203
{
206-
session.Factory.GetEntityPersister(clazz.FullName);
204+
return NHibernateUtil.Entity(clazz);
207205
}
208-
catch (MappingException)
206+
207+
if (type == null)
209208
{
210-
if (serializable)
211-
{
212-
return type;
213-
}
214-
else
215-
{
216-
throw new HibernateException("Could not determine a type for class: " + typename);
217-
}
209+
throw new HibernateException(
210+
"Could not determine a type for class: " + clazz.AssemblyQualifiedName);
218211
}
219-
return NHibernateUtil.Entity(clazz);
220-
}
221-
else
222-
{
223-
return type;
224212
}
213+
214+
return type;
225215
}
226216

227217
/// <summary>

src/NHibernate/Mapping/ByCode/Impl/TypeNameUtil.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public static class TypeNameUtil
88
public static string GetNhTypeName(this System.Type type)
99
{
1010
string typeName;
11-
IType nhType = TypeFactory.HeuristicType(type.AssemblyQualifiedName);
11+
IType nhType = TypeFactory.HeuristicType(type);
1212
if (nhType != null)
1313
{
1414
typeName = nhType.Name;
@@ -68,4 +68,4 @@ private static string GetTypeNameForMapping(System.Type type)
6868
return type.Name;
6969
}
7070
}
71-
}
71+
}

src/NHibernate/Type/TypeFactory.cs

Lines changed: 78 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -405,26 +405,11 @@ public static IType Basic(string name)
405405
/// </remarks>
406406
public static IType Basic(string name, IDictionary<string, string> parameters)
407407
{
408-
string typeName;
409-
410408
// Use the basic name (such as String or String(255)) to get the
411-
// instance of the IType object.
412-
IType returnType;
413-
if (typeByTypeOfName.TryGetValue(name, out returnType))
414-
{
415-
if (_obsoleteMessageByAlias.TryGetValue(name, out string obsoleteMessage))
416-
_log.Warn("{0} is obsolete. {1}", name, obsoleteMessage);
417-
418-
if (parameters?.Count > 0 && returnType is IParameterizedType)
419-
{
420-
// The type is parameterized, must apply the parameters to a new instance of the type.
421-
// Some built-in types have internal default constructor like StringType, so we need to
422-
// allow non-public constructors.
423-
returnType = (IType) Activator.CreateInstance(returnType.GetType(), true);
424-
InjectParameters(returnType, parameters);
425-
}
409+
// instance of the IType object.
410+
var returnType = GetBasicTypeByName(name, parameters);
411+
if (returnType != null)
426412
return returnType;
427-
}
428413

429414
// if we get to here then the basic type with the length or precision/scale
430415
// combination doesn't exists - so lets figure out which one we have and
@@ -442,7 +427,7 @@ public static IType Basic(string name, IDictionary<string, string> parameters)
442427
"TypeClassification.PrecisionScale", name, "It is not a valid Precision/Scale name");
443428
}
444429

445-
typeName = parsedName[0].Trim();
430+
string typeName = parsedName[0].Trim();
446431
byte precision = Byte.Parse(parsedName[1].Trim());
447432
byte scale = Byte.Parse(parsedName[2].Trim());
448433

@@ -459,7 +444,7 @@ public static IType Basic(string name, IDictionary<string, string> parameters)
459444
"TypeClassification.LengthOrScale", name, "It is not a valid Length or Scale name");
460445
}
461446

462-
typeName = parsedName[0].Trim();
447+
string typeName = parsedName[0].Trim();
463448
int length = Int32.Parse(parsedName[1].Trim());
464449

465450
returnType = BuiltInType(typeName, length);
@@ -478,6 +463,26 @@ public static IType Basic(string name, IDictionary<string, string> parameters)
478463
return returnType;
479464
}
480465

466+
private static IType GetBasicTypeByName(string name, IDictionary<string, string> parameters)
467+
{
468+
if (typeByTypeOfName.TryGetValue(name, out var returnType))
469+
{
470+
if (_obsoleteMessageByAlias.TryGetValue(name, out string obsoleteMessage))
471+
_log.Warn("{0} is obsolete. {1}", name, obsoleteMessage);
472+
473+
if (parameters?.Count > 0 && returnType is IParameterizedType)
474+
{
475+
// The type is parameterized, must apply the parameters to a new instance of the type.
476+
// Some built-in types have internal default constructor like StringType, so we need to
477+
// allow non-public constructors.
478+
returnType = (IType) Activator.CreateInstance(returnType.GetType(), true);
479+
InjectParameters(returnType, parameters);
480+
}
481+
return returnType;
482+
}
483+
return null;
484+
}
485+
481486
internal static IType BuiltInType(string typeName, int lengthOrScale)
482487
{
483488
GetNullableTypeWithLengthOrScale lengthOrScaleDelegate;
@@ -522,6 +527,23 @@ public static IType HeuristicType(string typeName)
522527
return HeuristicType(typeName, null);
523528
}
524529

530+
/// <summary>
531+
/// Uses heuristics to deduce a NHibernate type given a string naming the
532+
/// type.
533+
/// </summary>
534+
/// <param name="type"></param>
535+
/// <returns>An instance of <c>NHibernate.Type.IType</c></returns>
536+
/// <remarks>
537+
/// We check to see if it implements IType, ICompositeUserType, IUserType, ILifecycle (Association), or
538+
/// IPersistentEnum. If none of those are implemented then we will serialize the Type to the
539+
/// database using NHibernate.Type.SerializableType(typeName)
540+
/// </remarks>
541+
public static IType HeuristicType(System.Type type)
542+
{
543+
return GetBasicTypeByName(type.AssemblyQualifiedName, null)
544+
?? GetBySystemType(type, null, null);
545+
}
546+
525547
/// <summary>
526548
/// Uses heuristics to deduce a NHibernate type given a string naming the type.
527549
/// </summary>
@@ -532,7 +554,7 @@ public static IType HeuristicType(string typeName, IDictionary<string, string> p
532554
{
533555
return HeuristicType(typeName, parameters, null);
534556
}
535-
557+
536558
/// <summary>
537559
/// Uses heuristics to deduce a NHibernate type given a string naming the type.
538560
/// </summary>
@@ -546,55 +568,71 @@ public static IType HeuristicType(string typeName, IDictionary<string, string> p
546568

547569
if (type != null)
548570
return type;
549-
571+
550572
string[] parsedTypeName;
551-
TypeClassification typeClassification = GetTypeClassification(typeName);
573+
var typeClassification = GetTypeClassification(typeName);
552574
if (typeClassification == TypeClassification.LengthOrScale)
575+
{
553576
parsedTypeName = typeName.Split(LengthSplit);
577+
if (!int.TryParse(parsedTypeName[1], out int parsedLength))
578+
{
579+
throw new MappingException($"Could not parse length value '{parsedTypeName[1]}' as int for type '{typeName}'");
580+
}
581+
length = parsedLength;
582+
}
554583
else
555584
parsedTypeName = typeClassification == TypeClassification.PrecisionScale ? typeName.Split(PrecisionScaleSplit) : new[] { typeName };
556585

557-
558586
System.Type typeClass;
559587
try
560588
{
561589
typeClass = ReflectHelper.ClassForName(parsedTypeName[0]); //typeName);
562590
}
563591
catch (Exception)
564592
{
565-
typeClass = null;
593+
return null;
566594
}
567595

568-
if (typeClass == null)
569-
return null;
570-
596+
return GetBySystemType(typeClass, parameters, length);
597+
}
598+
599+
private static IType GetBySystemType(System.Type typeClass, IDictionary<string, string> parameters, int? length)
600+
{
571601
if (typeof(IType).IsAssignableFrom(typeClass))
572602
{
573603
try
574604
{
575-
type = (IType) Environment.ObjectsFactory.CreateInstance(typeClass);
605+
var type = (IType) Environment.ObjectsFactory.CreateInstance(typeClass);
606+
InjectParameters(type, parameters);
607+
608+
var obsolete = typeClass.GetCustomAttribute<ObsoleteAttribute>(false);
609+
if (obsolete != null)
610+
{
611+
_log.Warn("{0} ({1}) is obsolete. {2}", typeClass.FullName, type.Name, obsolete.Message);
612+
}
613+
614+
return type;
576615
}
577-
catch (Exception e)
616+
catch (HibernateException)
578617
{
579-
throw new MappingException("Could not instantiate IType " + typeClass.Name + ": " + e, e);
618+
throw;
580619
}
581-
InjectParameters(type, parameters);
582-
583-
var obsolete = typeClass.GetCustomAttribute<ObsoleteAttribute>(false);
584-
if (obsolete != null)
620+
catch (Exception e)
585621
{
586-
_log.Warn("{0} is obsolete. {1}", typeName, obsolete.Message);
622+
throw new MappingException("Could not instantiate IType " + typeClass.Name + ": " + e, e);
587623
}
588-
return type;
589624
}
625+
590626
if (typeof(ICompositeUserType).IsAssignableFrom(typeClass))
591627
{
592628
return new CompositeCustomType(typeClass, parameters);
593629
}
630+
594631
if (typeof(IUserType).IsAssignableFrom(typeClass))
595632
{
596633
return new CustomType(typeClass, parameters);
597634
}
635+
598636
if (typeof(ILifecycle).IsAssignableFrom(typeClass))
599637
{
600638
return NHibernateUtil.Entity(typeClass);
@@ -603,15 +641,12 @@ public static IType HeuristicType(string typeName, IDictionary<string, string> p
603641
var unwrapped = typeClass.UnwrapIfNullable();
604642
if (unwrapped.IsEnum)
605643
{
606-
return (IType) Activator.CreateInstance(typeof (EnumType<>).MakeGenericType(unwrapped));
644+
return (IType) Activator.CreateInstance(typeof(EnumType<>).MakeGenericType(unwrapped));
607645
}
608646

609647
if (!typeClass.IsSerializable)
610648
return null;
611649

612-
if (typeClassification == TypeClassification.LengthOrScale)
613-
return GetSerializableType(typeClass, Int32.Parse(parsedTypeName[1]));
614-
615650
if (length.HasValue)
616651
return GetSerializableType(typeClass, length.Value);
617652

@@ -690,7 +725,7 @@ public static NullableType GetSerializableType(System.Type serializableType)
690725
// So we should add the type with its other key in a later operation in order to ensure we cache the same
691726
// instance for both keys.
692727
var added = false;
693-
var type = (NullableType)typeByTypeOfName.GetOrAdd(
728+
var type = typeByTypeOfName.GetOrAdd(
694729
key,
695730
k =>
696731
{
@@ -703,7 +738,7 @@ public static NullableType GetSerializableType(System.Type serializableType)
703738
throw new HibernateException($"Another item with the key {type.Name} has already been added to typeByTypeOfName.");
704739
}
705740

706-
return type;
741+
return (NullableType) type;
707742
}
708743

709744
public static NullableType GetSerializableType(System.Type serializableType, int length)

src/NHibernate/Util/ReflectHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ public static IType ReflectedPropertyType(System.Type theClass, string name, str
293293

294294
var heuristicClass = propertyClass.UnwrapIfNullable();
295295

296-
return TypeFactory.HeuristicType(heuristicClass.AssemblyQualifiedName);
296+
return TypeFactory.HeuristicType(heuristicClass);
297297
}
298298

299299
/// <summary>

0 commit comments

Comments
 (0)