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
5 changes: 1 addition & 4 deletions src/NHibernate.Test/TypesTest/TimestampTypeFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ public void ObsoleteMessage()
var log = spy.GetWholeLog();
Assert.That(
log,
Does.Contain("NHibernate.Type.TimestampType is obsolete. Please use DateTimeType instead.").IgnoreCase);
Assert.That(
log,
Does.Not.Contain($"{NHibernateUtil.Timestamp.Name} is obsolete. Please use DateTimeType instead.").IgnoreCase);
Does.Contain($"NHibernate.Type.TimestampType ({NHibernateUtil.Timestamp.Name}) is obsolete. Please use DateTimeType instead.").IgnoreCase);
}
}
}
Expand Down
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;
}
}
}
}
85 changes: 62 additions & 23 deletions src/NHibernate/Type/TypeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,11 @@ public static IType Basic(string name)
/// This method will return null if the name is not found in the basicNameMap.
/// </remarks>
public static IType Basic(string name, IDictionary<string, string> parameters)
{
return Basic(name, parameters, parseName: true);
}

private static IType Basic(string name, IDictionary<string, string> parameters, bool parseName)
{
string typeName;

Expand All @@ -426,6 +431,9 @@ public static IType Basic(string name, IDictionary<string, string> parameters)
return returnType;
}

if (!parseName)
return null;

// if we get to here then the basic type with the length or precision/scale
// combination doesn't exists - so lets figure out which one we have and
// invoke the appropriate delegate
Expand Down Expand Up @@ -522,6 +530,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 +556,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 +570,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)
{
typeClass = null;
return null;
}
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, parseName: false);

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} ({1}) is obsolete. {2}", typeClass.Namespace + "." + typeClass.Name, type.Name, 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,15 +645,12 @@ 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);

Expand Down Expand Up @@ -690,7 +729,7 @@ public static NullableType GetSerializableType(System.Type serializableType)
// 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 All @@ -703,7 +742,7 @@ public static NullableType GetSerializableType(System.Type serializableType)
throw new HibernateException($"Another item with the key {type.Name} has already been added to typeByTypeOfName.");
}

return type;
return (NullableType) type;
}

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