Skip to content

Avoid some cases of Type -> string -> Type conversion in Mapping By Code #2003

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Feb 10, 2019
4 changes: 2 additions & 2 deletions src/NHibernate/Mapping/ByCode/Impl/TypeNameUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public static class TypeNameUtil
public static string GetNhTypeName(this System.Type type)
{
string typeName;
IType nhType = TypeFactory.HeuristicType(type.AssemblyQualifiedName);
IType nhType = TypeFactory.HeuristicType(type);
if (nhType != null)
{
typeName = nhType.Name;
Expand Down Expand Up @@ -68,4 +68,4 @@ private static string GetTypeNameForMapping(System.Type type)
return type.Name;
}
}
}
}
84 changes: 60 additions & 24 deletions src/NHibernate/Type/TypeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,22 @@ public static IType HeuristicType(string typeName)
return HeuristicType(typeName, null);
}

/// <summary>
/// Uses heuristics to deduce a NHibernate type given a string naming the
/// type.
/// </summary>
/// <param name="type"></param>
/// <returns>An instance of <c>NHibernate.Type.IType</c></returns>
/// <remarks>
/// We check to see if it implements IType, ICompositeUserType, IUserType, ILifecycle (Association), or
/// IPersistentEnum. If none of those are implemented then we will serialize the Type to the
/// database using NHibernate.Type.SerializableType(typeName)
/// </remarks>
public static IType HeuristicType(System.Type type)
{
return HeuristicType(type, parameters: null, length: null);
}

/// <summary>
/// Uses heuristics to deduce a NHibernate type given a string naming the type.
/// </summary>
Expand All @@ -532,7 +548,7 @@ public static IType HeuristicType(string typeName, IDictionary<string, string> p
{
return HeuristicType(typeName, parameters, null);
}

/// <summary>
/// Uses heuristics to deduce a NHibernate type given a string naming the type.
/// </summary>
Expand All @@ -546,46 +562,64 @@ public static IType HeuristicType(string typeName, IDictionary<string, string> p

if (type != null)
return type;

string[] parsedTypeName;
TypeClassification typeClassification = GetTypeClassification(typeName);
var typeClassification = GetTypeClassification(typeName);
if (typeClassification == TypeClassification.LengthOrScale)
{
parsedTypeName = typeName.Split(LengthSplit);
if (!int.TryParse(parsedTypeName[1], out int parsedLength))
{
throw new MappingException($"Could not parse length value '{parsedTypeName[1]}' as int for type '{typeName}'");
}
length = parsedLength;
}
else
parsedTypeName = typeClassification == TypeClassification.PrecisionScale ? typeName.Split(PrecisionScaleSplit) : new[] { typeName };


System.Type typeClass;
try
{
typeClass = ReflectHelper.ClassForName(parsedTypeName[0]); //typeName);
}
catch (Exception)
catch (Exception ex)
{
typeClass = null;
throw new MappingException("Could not find IType for given" + typeName + ": " + ex, ex);
}
return HeuristicType(typeClass, parameters, length, false);
}

if (typeClass == null)
return null;

private static IType HeuristicType(System.Type typeClass, IDictionary<string, string> parameters, int? length, bool tryBasic = true)
{
if(tryBasic)
{
IType type = Basic(typeClass.AssemblyQualifiedName, parameters);

if (type != null)
return type;
}
if (typeof(IType).IsAssignableFrom(typeClass))
{
try
{
type = (IType) Environment.ObjectsFactory.CreateInstance(typeClass);
var type = (IType) Environment.ObjectsFactory.CreateInstance(typeClass);
InjectParameters(type, parameters);

var obsolete = typeClass.GetCustomAttribute<ObsoleteAttribute>(false);
if (obsolete != null)
{
_log.Warn("{0} is obsolete. {1}", typeClass.AssemblyQualifiedName, obsolete.Message);
}
return type;
}
catch (Exception e)
catch (HibernateException)
{
throw new MappingException("Could not instantiate IType " + typeClass.Name + ": " + e, e);
throw;
}
InjectParameters(type, parameters);

var obsolete = typeClass.GetCustomAttribute<ObsoleteAttribute>(false);
if (obsolete != null)
catch (Exception e)
{
_log.Warn("{0} is obsolete. {1}", typeName, obsolete.Message);
throw new MappingException("Could not instantiate IType " + typeClass.Name + ": " + e, e);
}
return type;
}
if (typeof(ICompositeUserType).IsAssignableFrom(typeClass))
{
Expand All @@ -603,19 +637,16 @@ public static IType HeuristicType(string typeName, IDictionary<string, string> p
var unwrapped = typeClass.UnwrapIfNullable();
if (unwrapped.IsEnum)
{
return (IType) Activator.CreateInstance(typeof (EnumType<>).MakeGenericType(unwrapped));
return (IType) Activator.CreateInstance(typeof(EnumType<>).MakeGenericType(unwrapped));
}

if (!typeClass.IsSerializable)
return null;

if (typeClassification == TypeClassification.LengthOrScale)
return GetSerializableType(typeClass, Int32.Parse(parsedTypeName[1]));

if (length.HasValue)
return GetSerializableType(typeClass, length.Value);

return GetSerializableType(typeClass);
return GetSerializedOrBasicType(typeClass);
}

/// <summary>
Expand Down Expand Up @@ -683,14 +714,19 @@ private static NullableType GetType(NullableType defaultUnqualifiedType, byte pr
/// </para>
/// </remarks>
public static NullableType GetSerializableType(System.Type serializableType)
{
return (NullableType) GetSerializedOrBasicType(serializableType);
}

private static IType GetSerializedOrBasicType(System.Type serializableType)
{
var key = serializableType.AssemblyQualifiedName;

// The value factory may be run concurrently, but only one resulting value will be yielded to all threads.
// So we should add the type with its other key in a later operation in order to ensure we cache the same
// instance for both keys.
var added = false;
var type = (NullableType)typeByTypeOfName.GetOrAdd(
var type = typeByTypeOfName.GetOrAdd(
key,
k =>
{
Expand Down