Skip to content

Commit 87963fe

Browse files
authored
Fix deserialization for same type proxy association (#2720)
1 parent 19a4073 commit 87963fe

File tree

6 files changed

+311
-2
lines changed

6 files changed

+311
-2
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.IO;
12+
using System.Runtime.Serialization;
13+
using System.Runtime.Serialization.Formatters.Binary;
14+
using NHibernate.Cfg.MappingSchema;
15+
using NHibernate.Mapping.ByCode;
16+
using NUnit.Framework;
17+
18+
namespace NHibernate.Test.NHSpecificTest.GH2673
19+
{
20+
using System.Threading.Tasks;
21+
[TestFixture(true)]
22+
[TestFixture(false)]
23+
public class FixtureAsync : TestCaseMappingByCode
24+
{
25+
private readonly bool _withLazyProperties;
26+
27+
public FixtureAsync(bool withLazyProperties)
28+
{
29+
_withLazyProperties = withLazyProperties;
30+
}
31+
32+
protected override void OnSetUp()
33+
{
34+
using (var session = OpenSession())
35+
using (var transaction = session.BeginTransaction())
36+
{
37+
var role1 = new Role {Id = 1, Name = "role1"};
38+
session.Save(role1);
39+
var role2 = new Role {Id = 2, Name = "role2"};
40+
session.Save(role2);
41+
42+
var r1 = new Resource() {Id = 1, Name = "r1", ResourceRole = role1};
43+
session.Save(r1);
44+
45+
var r2 = new Resource() {Id = 2, Name = "r2", ResourceRole = role2};
46+
session.Save(r2);
47+
48+
var r3 = new Resource() {Id = 3, Name = "r3", ResourceRole = role2};
49+
session.Save(r3);
50+
51+
r1.Manager = r2;
52+
r2.Manager = r3;
53+
r3.Manager = r1;
54+
transaction.Commit();
55+
}
56+
}
57+
58+
protected override void OnTearDown()
59+
{
60+
using (var session = OpenSession())
61+
using (var transaction = session.BeginTransaction())
62+
{
63+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
64+
65+
transaction.Commit();
66+
}
67+
}
68+
69+
[Test]
70+
public async Task DeserializeSameTypeAssociationWithInitializedProxyAndCircularReferencesAsync()
71+
{
72+
using (var session = OpenSession())
73+
{
74+
var r1 = await (session.LoadAsync<Resource>(1));
75+
var r2 = await (session.LoadAsync<Resource>(2));
76+
var r3 = await (session.LoadAsync<Resource>(3));
77+
78+
var list = await (session.QueryOver<Resource>()
79+
.Fetch(SelectMode.Fetch, res => res.Manager)
80+
.ListAsync());
81+
82+
try
83+
{
84+
var serialised = SpoofSerialization(list[0]);
85+
SpoofSerialization(session);
86+
}
87+
catch (SerializationException)
88+
{
89+
//Lazy properties case throws due to circular references. See GH-2563
90+
if (!_withLazyProperties)
91+
throw;
92+
}
93+
}
94+
}
95+
96+
[Test]
97+
public async Task DeserializeSameTypeAssociationWithInitializedAndNotInitializedProxyAsync()
98+
{
99+
using (var session = OpenSession())
100+
{
101+
var r1 = await (session.GetAsync<Resource>(1));
102+
var r2 = await (session.GetAsync<Resource>(2));
103+
var r1Name = r1.Name;
104+
var serialised = SpoofSerialization(r1);
105+
Assert.That(serialised.Name, Is.EqualTo("r1"));
106+
}
107+
}
108+
109+
private T SpoofSerialization<T>(T obj)
110+
{
111+
var formatter = new BinaryFormatter
112+
{
113+
#if !NETFX
114+
SurrogateSelector = new NHibernate.Util.SerializationHelper.SurrogateSelector()
115+
#endif
116+
};
117+
var stream = new MemoryStream();
118+
formatter.Serialize(stream, obj);
119+
120+
stream.Position = 0;
121+
122+
return (T) formatter.Deserialize(stream);
123+
}
124+
125+
protected override HbmMapping GetMappings()
126+
{
127+
var mapper = new ModelMapper();
128+
mapper.Class<Resource>(
129+
m =>
130+
{
131+
m.Table("ResTable");
132+
m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned));
133+
m.Property(x => x.Name, x => x.Lazy(_withLazyProperties));
134+
m.ManyToOne(x => x.Manager, x => x.ForeignKey("none"));
135+
m.ManyToOne(x => x.ResourceRole, x => x.ForeignKey("none"));
136+
});
137+
mapper.Class<Role>(
138+
m =>
139+
{
140+
m.Table("RoleTable");
141+
m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned));
142+
m.Property(x => x.Name);
143+
});
144+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
145+
}
146+
}
147+
}

src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public async Task EntityAndCustomTypeInConditionalResultAsync()
5959
using (var s = OpenSession())
6060
await ((from x in s.Query<Entity1>()
6161
let parent = x.Parent
62-
//NH-3005 - Contditional on custom type
62+
//NH-3005 - Conditional with custom type
6363
where (parent.IsChiusa ? x.CustomType : parent.CustomType) == x.CustomType
6464
select new
6565
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2673
4+
{
5+
[Serializable]
6+
public class Resource
7+
{
8+
public virtual int Id { get; set; }
9+
public virtual Resource Manager { get; set; }
10+
public virtual string Name { get; set; }
11+
public virtual Role ResourceRole { get; set; }
12+
}
13+
14+
[Serializable]
15+
public class Role
16+
{
17+
public virtual int Id { get; set; }
18+
public virtual string Name { get; set; }
19+
}
20+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
using System.IO;
2+
using System.Runtime.Serialization;
3+
using System.Runtime.Serialization.Formatters.Binary;
4+
using NHibernate.Cfg.MappingSchema;
5+
using NHibernate.Mapping.ByCode;
6+
using NUnit.Framework;
7+
8+
namespace NHibernate.Test.NHSpecificTest.GH2673
9+
{
10+
[TestFixture(true)]
11+
[TestFixture(false)]
12+
public class Fixture : TestCaseMappingByCode
13+
{
14+
private readonly bool _withLazyProperties;
15+
16+
public Fixture(bool withLazyProperties)
17+
{
18+
_withLazyProperties = withLazyProperties;
19+
}
20+
21+
protected override void OnSetUp()
22+
{
23+
using (var session = OpenSession())
24+
using (var transaction = session.BeginTransaction())
25+
{
26+
var role1 = new Role {Id = 1, Name = "role1"};
27+
session.Save(role1);
28+
var role2 = new Role {Id = 2, Name = "role2"};
29+
session.Save(role2);
30+
31+
var r1 = new Resource() {Id = 1, Name = "r1", ResourceRole = role1};
32+
session.Save(r1);
33+
34+
var r2 = new Resource() {Id = 2, Name = "r2", ResourceRole = role2};
35+
session.Save(r2);
36+
37+
var r3 = new Resource() {Id = 3, Name = "r3", ResourceRole = role2};
38+
session.Save(r3);
39+
40+
r1.Manager = r2;
41+
r2.Manager = r3;
42+
r3.Manager = r1;
43+
transaction.Commit();
44+
}
45+
}
46+
47+
protected override void OnTearDown()
48+
{
49+
using (var session = OpenSession())
50+
using (var transaction = session.BeginTransaction())
51+
{
52+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
53+
54+
transaction.Commit();
55+
}
56+
}
57+
58+
[Test]
59+
public void DeserializeSameTypeAssociationWithInitializedProxyAndCircularReferences()
60+
{
61+
using (var session = OpenSession())
62+
{
63+
var r1 = session.Load<Resource>(1);
64+
var r2 = session.Load<Resource>(2);
65+
var r3 = session.Load<Resource>(3);
66+
67+
var list = session.QueryOver<Resource>()
68+
.Fetch(SelectMode.Fetch, res => res.Manager)
69+
.List();
70+
71+
try
72+
{
73+
var serialised = SpoofSerialization(list[0]);
74+
SpoofSerialization(session);
75+
}
76+
catch (SerializationException)
77+
{
78+
//Lazy properties case throws due to circular references. See GH-2563
79+
if (!_withLazyProperties)
80+
throw;
81+
}
82+
}
83+
}
84+
85+
[Test]
86+
public void DeserializeSameTypeAssociationWithInitializedAndNotInitializedProxy()
87+
{
88+
using (var session = OpenSession())
89+
{
90+
var r1 = session.Get<Resource>(1);
91+
var r2 = session.Get<Resource>(2);
92+
var r1Name = r1.Name;
93+
var serialised = SpoofSerialization(r1);
94+
Assert.That(serialised.Name, Is.EqualTo("r1"));
95+
}
96+
}
97+
98+
private T SpoofSerialization<T>(T obj)
99+
{
100+
var formatter = new BinaryFormatter
101+
{
102+
#if !NETFX
103+
SurrogateSelector = new NHibernate.Util.SerializationHelper.SurrogateSelector()
104+
#endif
105+
};
106+
var stream = new MemoryStream();
107+
formatter.Serialize(stream, obj);
108+
109+
stream.Position = 0;
110+
111+
return (T) formatter.Deserialize(stream);
112+
}
113+
114+
protected override HbmMapping GetMappings()
115+
{
116+
var mapper = new ModelMapper();
117+
mapper.Class<Resource>(
118+
m =>
119+
{
120+
m.Table("ResTable");
121+
m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned));
122+
m.Property(x => x.Name, x => x.Lazy(_withLazyProperties));
123+
m.ManyToOne(x => x.Manager, x => x.ForeignKey("none"));
124+
m.ManyToOne(x => x.ResourceRole, x => x.ForeignKey("none"));
125+
});
126+
mapper.Class<Role>(
127+
m =>
128+
{
129+
m.Table("RoleTable");
130+
m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned));
131+
m.Property(x => x.Name);
132+
});
133+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
134+
}
135+
}
136+
}

src/NHibernate/Proxy/NHibernateProxyFactoryInfo.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,10 @@ public void GetObjectData(SerializationInfo info, StreamingContext context)
7474
info.AddValue(nameof(_componentIdType), _componentIdType);
7575
info.AddValue(nameof(_isClassProxy), _isClassProxy);
7676
}
77+
78+
internal NHibernateProxyFactoryInfo Clone()
79+
{
80+
return new NHibernateProxyFactoryInfo(_entityName, _persistentClass, _interfaces, _getIdentifierMethod, _setIdentifierMethod, _componentIdType, _isClassProxy);
81+
}
7782
}
7883
}

src/NHibernate/Proxy/NHibernateProxyObjectReference.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ public object GetRealObject(StreamingContext context)
5151
[SecurityCritical]
5252
public void GetObjectData(SerializationInfo info, StreamingContext context)
5353
{
54-
info.AddValue(nameof(_proxyFactoryInfo), _proxyFactoryInfo);
54+
//Save a copy as it seems IObjectReference deserialization can't properly handle multiple objects with the same reference
55+
info.AddValue(nameof(_proxyFactoryInfo), _proxyFactoryInfo.Clone());
5556
info.AddValue(nameof(_identifier), _identifier);
5657
info.AddValue(nameof(_implementation), _implementation);
5758
}

0 commit comments

Comments
 (0)