diff --git a/src/NHibernate.Test/NHSpecificTest/NH3957/ResultTransformerEqualityFixture.cs b/src/NHibernate.Test/NHSpecificTest/NH3957/ResultTransformerEqualityFixture.cs
index cd0db9a6f14..d1900b57abc 100644
--- a/src/NHibernate.Test/NHSpecificTest/NH3957/ResultTransformerEqualityFixture.cs
+++ b/src/NHibernate.Test/NHSpecificTest/NH3957/ResultTransformerEqualityFixture.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.Reflection;
using NHibernate.Transform;
using NUnit.Framework;
@@ -8,53 +7,20 @@ namespace NHibernate.Test.NHSpecificTest.NH3957
[TestFixture]
public class ResultTransformerEqualityFixture
{
- ///
- /// Allows to simulate a hashcode collision. Issue would be unpractical to test otherwise.
- /// Hashcode collision must be supported for avoiding unexpected and hard to reproduce failures.
- ///
- private void TweakHashcode(System.Type transformerToTweak, object hasher)
- {
- var hasherTargetField = transformerToTweak.GetField("Hasher", BindingFlags.Static | BindingFlags.NonPublic);
- if (!_hasherBackup.ContainsKey(transformerToTweak))
- _hasherBackup.Add(transformerToTweak, hasherTargetField.GetValue(null));
-
- // Though hasher is a readonly field, this works at the time of this writing. If it starts breaking and cannot be fixed,
- // ignore those tests or throw them away.
- hasherTargetField.SetValue(null, hasher);
- }
-
- private Dictionary _hasherBackup = new Dictionary();
-
- [SetUp]
- public void Setup()
- {
- var hasherForAll = typeof(AliasToEntityMapResultTransformer)
- .GetField("Hasher", BindingFlags.Static | BindingFlags.NonPublic)
- .GetValue(null);
- TweakHashcode(typeof(DistinctRootEntityResultTransformer), hasherForAll);
- TweakHashcode(typeof(PassThroughResultTransformer), hasherForAll);
- TweakHashcode(typeof(RootEntityResultTransformer), hasherForAll);
- TweakHashcode(typeof(ToListResultTransformer), hasherForAll);
- }
-
- [TearDown]
- public void TearDown()
- {
- // Restore those types hashcode. (Avoid impacting perf of other tests, avoid second level query cache
- // issues if it was holding cached entries (but would mean some tests have not cleaned up properly).)
- foreach(var backup in _hasherBackup)
- {
- TweakHashcode(backup.Key, backup.Value);
- }
- }
-
+ public class CustomAliasToEntityMapResultTransformer : AliasToEntityMapResultTransformer { }
+ public class CustomDistinctRootEntityResultTransformer : DistinctRootEntityResultTransformer { }
+ public class CustomPassThroughResultTransformer : PassThroughResultTransformer { }
+ public class CustomRootEntityResultTransformer : RootEntityResultTransformer { }
+
// Non reg test case
[Test]
public void AliasToEntityMapEquality()
{
var transf1 = new AliasToEntityMapResultTransformer();
var transf2 = new AliasToEntityMapResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2, };
+ Assert.That(set.Count, Is.EqualTo(1));
Assert.IsTrue(transf1.Equals(transf2));
Assert.IsTrue(transf2.Equals(transf1));
}
@@ -63,8 +29,11 @@ public void AliasToEntityMapEquality()
public void AliasToEntityMapAndDistinctRootEntityInequality()
{
var transf1 = new AliasToEntityMapResultTransformer();
- var transf2 = new DistinctRootEntityResultTransformer();
+ var transf2 = new CustomAliasToEntityMapResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2, };
+ Assert.That(transf1.GetHashCode(), Is.EqualTo(transf2.GetHashCode()), "prerequisite");
+ Assert.That(set.Count, Is.EqualTo(2));
Assert.IsFalse(transf1.Equals(transf2));
Assert.IsFalse(transf2.Equals(transf1));
}
@@ -75,18 +44,35 @@ public void DistinctRootEntityEquality()
{
var transf1 = new DistinctRootEntityResultTransformer();
var transf2 = new DistinctRootEntityResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2, };
+ Assert.That(set.Count, Is.EqualTo(1));
Assert.IsTrue(transf1.Equals(transf2));
Assert.IsTrue(transf2.Equals(transf1));
}
+ [Test]
+ public void DistinctRootEntityEqualityInequality()
+ {
+ var transf1 = new DistinctRootEntityResultTransformer();
+ var transf2 = new CustomDistinctRootEntityResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2, };
+
+ Assert.That(transf1.GetHashCode(), Is.EqualTo(transf2.GetHashCode()), "prerequisite");
+ Assert.That(set.Count, Is.EqualTo(2));
+ Assert.IsFalse(transf1.Equals(transf2));
+ Assert.IsFalse(transf2.Equals(transf1));
+ }
+
// Non reg test case
[Test]
public void PassThroughEquality()
{
var transf1 = new PassThroughResultTransformer();
var transf2 = new PassThroughResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2, };
+ Assert.That(set.Count, Is.EqualTo(1));
Assert.IsTrue(transf1.Equals(transf2));
Assert.IsTrue(transf2.Equals(transf1));
}
@@ -95,8 +81,11 @@ public void PassThroughEquality()
public void PassThroughAndRootEntityInequality()
{
var transf1 = new PassThroughResultTransformer();
- var transf2 = new RootEntityResultTransformer();
-
+ var transf2 = new CustomPassThroughResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2, };
+
+ Assert.That(transf1.GetHashCode(), Is.EqualTo(transf2.GetHashCode()), "prerequisite");
+ Assert.That(set.Count, Is.EqualTo(2));
Assert.IsFalse(transf1.Equals(transf2));
Assert.IsFalse(transf2.Equals(transf1));
}
@@ -107,7 +96,9 @@ public void RootEntityEquality()
{
var transf1 = new RootEntityResultTransformer();
var transf2 = new RootEntityResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2, };
+ Assert.That(set.Count, Is.EqualTo(1));
Assert.IsTrue(transf1.Equals(transf2));
Assert.IsTrue(transf2.Equals(transf1));
}
@@ -116,8 +107,11 @@ public void RootEntityEquality()
public void RootEntityAndToListInequality()
{
var transf1 = new RootEntityResultTransformer();
- var transf2 = new ToListResultTransformer();
-
+ var transf2 = new CustomRootEntityResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2, };
+
+ Assert.That(transf1.GetHashCode(), Is.EqualTo(transf2.GetHashCode()), "prerequisite");
+ Assert.That(set.Count, Is.EqualTo(2));
Assert.IsFalse(transf1.Equals(transf2));
Assert.IsFalse(transf2.Equals(transf1));
}
@@ -128,9 +122,11 @@ public void ToListEquality()
{
var transf1 = new ToListResultTransformer();
var transf2 = new ToListResultTransformer();
+ HashSet set = new HashSet() { transf1, transf2 };
+ Assert.That(set.Count, Is.EqualTo(1));
Assert.IsTrue(transf1.Equals(transf2));
Assert.IsTrue(transf2.Equals(transf1));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Criterion/CriteriaSpecification.cs b/src/NHibernate/Criterion/CriteriaSpecification.cs
index a274a6cbeb3..a4d2505a891 100644
--- a/src/NHibernate/Criterion/CriteriaSpecification.cs
+++ b/src/NHibernate/Criterion/CriteriaSpecification.cs
@@ -31,10 +31,10 @@ public static class CriteriaSpecification
static CriteriaSpecification()
{
- AliasToEntityMap = new AliasToEntityMapResultTransformer();
- RootEntity = new RootEntityResultTransformer();
- DistinctRootEntity = new DistinctRootEntityResultTransformer();
- Projection = new PassThroughResultTransformer();
+ AliasToEntityMap = AliasToEntityMapResultTransformer.Instance;
+ RootEntity = RootEntityResultTransformer.Instance;
+ DistinctRootEntity = DistinctRootEntityResultTransformer.Instance;
+ Projection = PassThroughResultTransformer.Instance;
InnerJoin = JoinType.InnerJoin;
FullJoin = JoinType.FullJoin;
LeftJoin = JoinType.LeftOuterJoin;
diff --git a/src/NHibernate/Transform/AliasToEntityMapResultTransformer.cs b/src/NHibernate/Transform/AliasToEntityMapResultTransformer.cs
index 3fe474856fe..0497d292a51 100644
--- a/src/NHibernate/Transform/AliasToEntityMapResultTransformer.cs
+++ b/src/NHibernate/Transform/AliasToEntityMapResultTransformer.cs
@@ -6,7 +6,7 @@ namespace NHibernate.Transform
[Serializable]
public class AliasToEntityMapResultTransformer : AliasedTupleSubsetResultTransformer
{
- private static readonly object Hasher = new object();
+ internal static readonly AliasToEntityMapResultTransformer Instance = new AliasToEntityMapResultTransformer();
public override object TransformTuple(object[] tuple, string[] aliases)
{
@@ -37,18 +37,16 @@ public override bool IsTransformedValueATupleElement(string[] aliases, int tuple
public override bool Equals(object obj)
{
- if (obj == null || obj.GetHashCode() != Hasher.GetHashCode())
- {
+ if (ReferenceEquals(obj, this))
+ return true;
+ if (obj == null)
return false;
- }
- // NH-3957: do not rely on hashcode alone.
- // Must be the exact same type
- return obj.GetType() == typeof(AliasToEntityMapResultTransformer);
+ return obj.GetType() == GetType();
}
public override int GetHashCode()
{
- return Hasher.GetHashCode();
+ return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(Instance);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Transform/CacheableResultTransformer.cs b/src/NHibernate/Transform/CacheableResultTransformer.cs
index 91ff637aba0..1174f5d9478 100644
--- a/src/NHibernate/Transform/CacheableResultTransformer.cs
+++ b/src/NHibernate/Transform/CacheableResultTransformer.cs
@@ -19,7 +19,7 @@ public class CacheableResultTransformer : IResultTransformer
// is private (as it should be for a singleton)
//private const PassThroughResultTransformer ACTUAL_TRANSFORMER =
// PassThroughResultTransformer.INSTANCE;
- private readonly PassThroughResultTransformer _actualTransformer = new PassThroughResultTransformer();
+ private readonly PassThroughResultTransformer _actualTransformer = PassThroughResultTransformer.Instance;
public bool AutoDiscoverTypes { get; }
@@ -358,7 +358,7 @@ public override bool Equals(object o)
if (this == o)
return true;
- if (o == null || typeof (CacheableResultTransformer) != o.GetType())
+ if (o == null || GetType() != o.GetType())
return false;
var that = (CacheableResultTransformer) o;
diff --git a/src/NHibernate/Transform/DistinctRootEntityResultTransformer.cs b/src/NHibernate/Transform/DistinctRootEntityResultTransformer.cs
index c4a65ce87cb..6cd4211bab1 100644
--- a/src/NHibernate/Transform/DistinctRootEntityResultTransformer.cs
+++ b/src/NHibernate/Transform/DistinctRootEntityResultTransformer.cs
@@ -10,7 +10,7 @@ namespace NHibernate.Transform
public class DistinctRootEntityResultTransformer : IResultTransformer, ITupleSubsetResultTransformer
{
private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(DistinctRootEntityResultTransformer));
- private static readonly object Hasher = new object();
+ internal static readonly DistinctRootEntityResultTransformer Instance = new DistinctRootEntityResultTransformer();
internal sealed class Identity
{
@@ -62,33 +62,26 @@ public IList TransformList(IList list)
public bool[] IncludeInTransform(String[] aliases, int tupleLength)
{
- //return RootEntityResultTransformer.INSTANCE.includeInTransform(aliases, tupleLength);
- var transformer = new RootEntityResultTransformer();
- return transformer.IncludeInTransform(aliases, tupleLength);
+ return RootEntityResultTransformer.Instance.IncludeInTransform(aliases, tupleLength);
}
-
public bool IsTransformedValueATupleElement(String[] aliases, int tupleLength)
{
- //return RootEntityResultTransformer.INSTANCE.isTransformedValueATupleElement(null, tupleLength);
- var transformer = new RootEntityResultTransformer();
- return transformer.IsTransformedValueATupleElement(null, tupleLength);
+ return RootEntityResultTransformer.Instance.IsTransformedValueATupleElement(null, tupleLength);
}
public override bool Equals(object obj)
{
- if (obj == null || obj.GetHashCode() != Hasher.GetHashCode())
- {
+ if (ReferenceEquals(obj, this))
+ return true;
+ if (obj == null)
return false;
- }
- // NH-3957: do not rely on hashcode alone.
- // Must be the exact same type
- return obj.GetType() == typeof(DistinctRootEntityResultTransformer);
+ return obj.GetType() == GetType();
}
public override int GetHashCode()
{
- return Hasher.GetHashCode();
+ return RuntimeHelpers.GetHashCode(Instance);
}
}
}
diff --git a/src/NHibernate/Transform/PassThroughResultTransformer.cs b/src/NHibernate/Transform/PassThroughResultTransformer.cs
index 0a116b51aff..72454e2883a 100644
--- a/src/NHibernate/Transform/PassThroughResultTransformer.cs
+++ b/src/NHibernate/Transform/PassThroughResultTransformer.cs
@@ -7,7 +7,7 @@ namespace NHibernate.Transform
[Serializable]
public class PassThroughResultTransformer : IResultTransformer, ITupleSubsetResultTransformer
{
- private static readonly object Hasher = new object();
+ internal static readonly PassThroughResultTransformer Instance = new PassThroughResultTransformer();
#region IResultTransformer Members
@@ -58,21 +58,18 @@ internal object[] UntransformToTuple(object transformed, bool isSingleResult)
return isSingleResult ? new[] {transformed} : (object[]) transformed;
}
-
public override bool Equals(object obj)
{
- if (obj == null || obj.GetHashCode() != Hasher.GetHashCode())
- {
+ if (ReferenceEquals(obj, this))
+ return true;
+ if (obj == null)
return false;
- }
- // NH-3957: do not rely on hashcode alone.
- // Must be the exact same type
- return obj.GetType() == typeof(PassThroughResultTransformer);
+ return obj.GetType() == GetType();
}
public override int GetHashCode()
{
- return Hasher.GetHashCode();
+ return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(Instance);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Transform/RootEntityResultTransformer.cs b/src/NHibernate/Transform/RootEntityResultTransformer.cs
index b8468ebbc0c..5d2ecc9b13f 100644
--- a/src/NHibernate/Transform/RootEntityResultTransformer.cs
+++ b/src/NHibernate/Transform/RootEntityResultTransformer.cs
@@ -7,7 +7,7 @@ namespace NHibernate.Transform
[Serializable]
public class RootEntityResultTransformer : IResultTransformer, ITupleSubsetResultTransformer
{
- private static readonly object Hasher = new object();
+ internal static readonly RootEntityResultTransformer Instance = new RootEntityResultTransformer();
public object TransformTuple(object[] tuple, string[] aliases)
{
@@ -19,13 +19,11 @@ public IList TransformList(IList collection)
return collection;
}
-
public bool IsTransformedValueATupleElement(String[] aliases, int tupleLength)
{
return true;
}
-
public bool[] IncludeInTransform(String[] aliases, int tupleLength)
{
bool[] includeInTransform;
@@ -43,18 +41,16 @@ public bool[] IncludeInTransform(String[] aliases, int tupleLength)
public override bool Equals(object obj)
{
- if (obj == null || obj.GetHashCode() != Hasher.GetHashCode())
- {
+ if (ReferenceEquals(obj, this))
+ return true;
+ if (obj == null)
return false;
- }
- // NH-3957: do not rely on hashcode alone.
- // Must be the exact same type
- return obj.GetType() == typeof(RootEntityResultTransformer);
+ return obj.GetType() == GetType();
}
public override int GetHashCode()
{
- return Hasher.GetHashCode();
+ return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(Instance);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Transform/ToListResultTransformer.cs b/src/NHibernate/Transform/ToListResultTransformer.cs
index e6ccc1977bd..03d6b93324c 100644
--- a/src/NHibernate/Transform/ToListResultTransformer.cs
+++ b/src/NHibernate/Transform/ToListResultTransformer.cs
@@ -11,7 +11,7 @@ namespace NHibernate.Transform
[Serializable]
public class ToListResultTransformer : IResultTransformer
{
- private static readonly object Hasher = new object();
+ internal static readonly ToListResultTransformer Instance = new ToListResultTransformer();
public object TransformTuple(object[] tuple, string[] aliases)
{
@@ -25,18 +25,16 @@ public IList TransformList(IList list)
public override bool Equals(object obj)
{
- if (obj == null || obj.GetHashCode() != Hasher.GetHashCode())
- {
+ if (ReferenceEquals(obj, this))
+ return true;
+ if (obj == null)
return false;
- }
- // NH-3957: do not rely on hashcode alone.
- // Must be the exact same type
- return obj.GetType() == typeof(ToListResultTransformer);
+ return obj.GetType() == GetType();
}
public override int GetHashCode()
{
- return Hasher.GetHashCode();
+ return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(Instance);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Transform/Transformers.cs b/src/NHibernate/Transform/Transformers.cs
index 92755f94db2..e139c709e8f 100644
--- a/src/NHibernate/Transform/Transformers.cs
+++ b/src/NHibernate/Transform/Transformers.cs
@@ -7,10 +7,10 @@ public static class Transformers
///
/// Each row of results is a map () from alias to values/entities
///
- public static readonly IResultTransformer AliasToEntityMap = new AliasToEntityMapResultTransformer();
+ public static readonly IResultTransformer AliasToEntityMap = AliasToEntityMapResultTransformer.Instance;
/// Each row of results is a
- public static readonly ToListResultTransformer ToList = new ToListResultTransformer();
+ public static readonly ToListResultTransformer ToList = ToListResultTransformer.Instance;
///
/// Creates a result transformer that will inject aliased values into instances
@@ -44,15 +44,15 @@ public static IResultTransformer AliasToBean()
return AliasToBean(typeof(T));
}
- public static readonly IResultTransformer DistinctRootEntity = new DistinctRootEntityResultTransformer();
+ public static readonly IResultTransformer DistinctRootEntity = DistinctRootEntityResultTransformer.Instance;
public static IResultTransformer AliasToBeanConstructor(System.Reflection.ConstructorInfo constructor)
{
return new AliasToBeanConstructorResultTransformer(constructor);
}
- public static readonly IResultTransformer PassThrough = new PassThroughResultTransformer();
+ public static readonly IResultTransformer PassThrough = PassThroughResultTransformer.Instance;
- public static readonly IResultTransformer RootEntity = new RootEntityResultTransformer();
+ public static readonly IResultTransformer RootEntity = RootEntityResultTransformer.Instance;
}
}