Skip to content

Commit 38ef836

Browse files
committed
Merge branch 'JKeyser-NH3487'
2 parents 04e99bc + 879ca6f commit 38ef836

File tree

6 files changed

+188
-7
lines changed

6 files changed

+188
-7
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3487
4+
{
5+
[Serializable]
6+
public class Entity
7+
{
8+
public virtual Key Id { get; set; }
9+
public virtual string Name { get; set; }
10+
11+
public override bool Equals(object obj)
12+
{
13+
var otherEntity = obj as Entity;
14+
if (otherEntity != null)
15+
{
16+
return otherEntity.Id.Equals(Id);
17+
}
18+
return false;
19+
}
20+
21+
public override int GetHashCode()
22+
{
23+
return Id.GetHashCode();
24+
}
25+
}
26+
27+
[Serializable]
28+
public class Key
29+
{
30+
public virtual int Id { get; set; }
31+
32+
public override bool Equals(object obj)
33+
{
34+
var otherEntity = obj as Key;
35+
if (otherEntity != null)
36+
{
37+
return otherEntity.Id == Id;
38+
}
39+
return false;
40+
}
41+
42+
public override int GetHashCode()
43+
{
44+
// Important to reproduce the problem - forces the keys to collide in the hash map
45+
return 1;
46+
}
47+
}
48+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System.IO;
2+
using System.Runtime.Serialization;
3+
using System.Runtime.Serialization.Formatters.Binary;
4+
using NUnit.Framework;
5+
6+
namespace NHibernate.Test.NHSpecificTest.NH3487
7+
{
8+
[TestFixture]
9+
public class Fixture : BugTestCase
10+
{
11+
private Key _key1;
12+
private Key _key2;
13+
14+
public override string BugNumber
15+
{
16+
get { return "NH3487"; }
17+
}
18+
19+
protected override void OnSetUp()
20+
{
21+
using (ISession session = OpenSession())
22+
{
23+
using (ITransaction transaction = session.BeginTransaction())
24+
{
25+
_key1 = new Key {Id = 1};
26+
var entity1 = new Entity {Id = _key1, Name = "Bob1"};
27+
session.Save(entity1);
28+
29+
_key2 = new Key {Id = 2};
30+
var entity2 = new Entity {Id = _key2, Name = "Bob2"};
31+
session.Save(entity2);
32+
33+
session.Flush();
34+
transaction.Commit();
35+
}
36+
}
37+
}
38+
39+
protected override void OnTearDown()
40+
{
41+
using (ISession session = OpenSession())
42+
{
43+
using (ITransaction transaction = session.BeginTransaction())
44+
{
45+
session.Delete("from System.Object");
46+
47+
session.Flush();
48+
transaction.Commit();
49+
}
50+
}
51+
}
52+
53+
[Test]
54+
public void CanDeserializeSessionWithEntityHashCollision()
55+
{
56+
IFormatter formatter = new BinaryFormatter();
57+
byte[] serializedSessionArray;
58+
59+
using (ISession session = OpenSession())
60+
{
61+
using (session.BeginTransaction())
62+
{
63+
session.Get<Entity>(_key1);
64+
session.Get<Entity>(_key2);
65+
}
66+
67+
session.Disconnect();
68+
using (var serializationStream = new MemoryStream())
69+
{
70+
formatter.Serialize(serializationStream, session);
71+
serializationStream.Close();
72+
serializedSessionArray = serializationStream.ToArray();
73+
}
74+
}
75+
76+
using (var serializationStream = new MemoryStream(serializedSessionArray))
77+
{
78+
formatter.Deserialize(serializationStream);
79+
}
80+
81+
}
82+
}
83+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test" namespace="NHibernate.Test.NHSpecificTest.NH3487">
3+
4+
<class name="Entity">
5+
<composite-id name="Id">
6+
<key-property name="Id" />
7+
</composite-id>
8+
<property name="Name" not-null="true" />
9+
</class>
10+
11+
</hibernate-mapping>

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,8 @@
693693
<Compile Include="NHSpecificTest\BagWithLazyExtraAndFilter\Fixture.cs" />
694694
<Compile Include="Linq\ByMethod\DistinctTests.cs" />
695695
<Compile Include="Component\Basic\ComponentWithUniqueConstraintTests.cs" />
696+
<Compile Include="NHSpecificTest\NH3487\Entity.cs" />
697+
<Compile Include="NHSpecificTest\NH3487\Fixture.cs" />
696698
<Compile Include="NHSpecificTest\NH3620\Fixture.cs" />
697699
<Compile Include="NHSpecificTest\NH3620\TwoBlobs.cs" />
698700
<Compile Include="NHSpecificTest\NH3455\Address.cs" />
@@ -3068,6 +3070,7 @@
30683070
<EmbeddedResource Include="Unionsubclass\DatabaseKeyword.hbm.xml" />
30693071
<EmbeddedResource Include="NHSpecificTest\NH1082\Mappings.hbm.xml" />
30703072
<EmbeddedResource Include="NHSpecificTest\NH3571\Mappings.hbm.xml" />
3073+
<EmbeddedResource Include="NHSpecificTest\NH3487\Mappings.hbm.xml" />
30713074
<EmbeddedResource Include="NHSpecificTest\NH2692\Mappings.hbm.xml">
30723075
<SubType>Designer</SubType>
30733076
</EmbeddedResource>

src/NHibernate/Tuple/EntityModeToTuplizerMapping.cs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,43 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Runtime.Serialization;
34
using NHibernate.Util;
45

56
namespace NHibernate.Tuple
67
{
78
/// <summary> Centralizes handling of <see cref="EntityMode"/> to <see cref="ITuplizer"/> mappings. </summary>
89
[Serializable]
9-
public abstract class EntityModeToTuplizerMapping
10+
public abstract class EntityModeToTuplizerMapping : IDeserializationCallback
1011
{
12+
1113
// NH-1660
12-
private readonly IDictionary<EntityMode, ITuplizer> tuplizers
13-
= new LinkedHashMap<EntityMode, ITuplizer>(5, new EntityModeEqualityComparer());
14+
private readonly IDictionary<EntityMode, ITuplizer> _tuplizers
15+
= new LinkedHashMap<EntityMode, ITuplizer>(5, new EntityModeEqualityComparer());
16+
17+
/// <summary>
18+
/// This class might get called during serialization, and may therefore need to deserialize on-demand.
19+
/// </summary>
20+
[NonSerialized] private bool _isFullyDeserialized;
21+
22+
23+
protected EntityModeToTuplizerMapping()
24+
{
25+
_isFullyDeserialized = true;
26+
}
1427

1528
protected internal void AddTuplizer(EntityMode entityMode, ITuplizer tuplizer)
1629
{
17-
tuplizers[entityMode] = tuplizer;
30+
EnsureFullyDeserialized();
31+
_tuplizers[entityMode] = tuplizer;
1832
}
1933

2034
/// <summary> Given a supposed instance of an entity/component, guess its entity mode. </summary>
2135
/// <param name="obj">The supposed instance of the entity/component.</param>
2236
/// <returns> The guessed entity mode. </returns>
2337
public virtual EntityMode? GuessEntityMode(object obj)
2438
{
25-
foreach (KeyValuePair<EntityMode, ITuplizer> entry in tuplizers)
39+
EnsureFullyDeserialized();
40+
foreach (KeyValuePair<EntityMode, ITuplizer> entry in _tuplizers)
2641
{
2742
ITuplizer tuplizer = entry.Value;
2843
if (tuplizer.IsInstance(obj))
@@ -41,8 +56,9 @@ protected internal void AddTuplizer(EntityMode entityMode, ITuplizer tuplizer)
4156
/// <returns> The tuplizer, or null if not found. </returns>
4257
public virtual ITuplizer GetTuplizerOrNull(EntityMode entityMode)
4358
{
59+
EnsureFullyDeserialized();
4460
ITuplizer result;
45-
tuplizers.TryGetValue(entityMode, out result);
61+
_tuplizers.TryGetValue(entityMode, out result);
4662
return result;
4763
}
4864

@@ -65,5 +81,19 @@ public virtual ITuplizer GetTuplizer(EntityMode entityMode)
6581
}
6682
return tuplizer;
6783
}
84+
85+
private void EnsureFullyDeserialized()
86+
{
87+
if (!_isFullyDeserialized)
88+
{
89+
((IDeserializationCallback) this).OnDeserialization(this);
90+
}
91+
}
92+
93+
void IDeserializationCallback.OnDeserialization(object sender)
94+
{
95+
((IDeserializationCallback) _tuplizers).OnDeserialization(sender);
96+
_isFullyDeserialized = true;
97+
}
6898
}
6999
}

src/NHibernate/Util/LinkedHashMap.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections;
33
using System.Collections.Generic;
44
using System.Diagnostics;
5+
using System.Runtime.Serialization;
56
using System.Text;
67
using NHibernate.DebugHelpers;
78

@@ -18,7 +19,7 @@ namespace NHibernate.Util
1819
/// </remarks>
1920
[DebuggerTypeProxy(typeof(CollectionProxy<>))]
2021
[Serializable]
21-
public class LinkedHashMap<TKey, TValue> : IDictionary<TKey, TValue>
22+
public class LinkedHashMap<TKey, TValue> : IDictionary<TKey, TValue>, IDeserializationCallback
2223
{
2324
[Serializable]
2425
protected class Entry
@@ -359,6 +360,11 @@ private bool RemoveImpl(TKey key)
359360
return result;
360361
}
361362

363+
void IDeserializationCallback.OnDeserialization(object sender)
364+
{
365+
((IDeserializationCallback)entries).OnDeserialization(sender);
366+
}
367+
362368
#region System.Object Members
363369

364370
public override string ToString()

0 commit comments

Comments
 (0)