@@ -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 )
0 commit comments