From 4b6d874f901cabeea79e66d8d38e829fe093a7e5 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Fri, 2 Apr 2021 08:37:46 +0300 Subject: [PATCH 1/2] Fix deserialization for same type proxy association --- .../Async/NHSpecificTest/GH2673/Fixture.cs | 145 ++++++++++++++++++ .../NHSpecificTest/GH2707/FixtureByCode.cs | 2 +- .../NHSpecificTest/GH2673/Entity.cs | 20 +++ .../NHSpecificTest/GH2673/Fixture.cs | 134 ++++++++++++++++ .../Proxy/NHibernateProxyFactoryInfo.cs | 5 + .../Proxy/NHibernateProxyObjectReference.cs | 3 +- 6 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2673/Entity.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs new file mode 100644 index 00000000000..f4f94bc8eba --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs @@ -0,0 +1,145 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH2673 +{ + using System.Threading.Tasks; + [TestFixture(true)] + [TestFixture(false)] + public class FixtureAsync : TestCaseMappingByCode + { + private readonly bool _withLazyProperties; + + public FixtureAsync(bool withLazyProperties) + { + _withLazyProperties = withLazyProperties; + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var role1 = new Role {Id = 1, Name = "role1"}; + session.Save(role1); + var role2 = new Role {Id = 2, Name = "role2"}; + session.Save(role2); + + var r1 = new Resource() {Id = 1, Name = "r1", ResourceRole = role1}; + session.Save(r1); + + var r2 = new Resource() {Id = 2, Name = "r2", ResourceRole = role2}; + session.Save(r2); + + var r3 = new Resource() {Id = 3, Name = "r3", ResourceRole = role2}; + session.Save(r3); + + r1.Manager = r2; + r2.Manager = r3; + r3.Manager = r1; + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + + transaction.Commit(); + } + } + + [Test] + public async Task DeserializeSameTypeAssociationWithInitializedProxyAndCircularReferencesAsync() + { + using (var session = OpenSession()) + { + var r1 = await (session.LoadAsync(1)); + var r2 = await (session.LoadAsync(2)); + var r3 = await (session.LoadAsync(3)); + + var list = await (session.QueryOver() + .Fetch(SelectMode.Fetch, res => res.Manager) + .ListAsync()); + + try + { + var serialised = SpoofSerialization(list[0]); + SpoofSerialization(session); + } + catch (SerializationException) + { + //Lazy properties case throws due to circular references. See GH-2563 + if (!_withLazyProperties) + throw; + } + } + } + + [Test] + public async Task DeserializeSameTypeAssociationWithInitializedAndNotInitializedProxyAsync() + { + using (var session = OpenSession()) + { + var r1 = await (session.GetAsync(1)); + var r2 = await (session.GetAsync(2)); + var r1Name = r1.Name; + var serialised = SpoofSerialization(r1); + Assert.That(serialised.Name, Is.EqualTo("r1")); + } + } + + private T SpoofSerialization(T obj) + { + var formatter = new BinaryFormatter + { +#if !NETFX + SurrogateSelector = new NHibernate.Util.SerializationHelper.SurrogateSelector() +#endif + }; + var stream = new MemoryStream(); + formatter.Serialize(stream, obj); + + stream.Position = 0; + + return (T) formatter.Deserialize(stream); + } + + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class( + m => + { + m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned)); + m.Property(x => x.Name, x => x.Lazy(_withLazyProperties)); + m.ManyToOne(x => x.Manager, x => x.ForeignKey("none")); + m.ManyToOne(x => x.ResourceRole, x => x.ForeignKey("none")); + }); + mapper.Class( + m => + { + m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned)); + m.Property(x => x.Name); + }); + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + } +} diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs index ed8a151bd42..d4914df99c2 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2707/FixtureByCode.cs @@ -59,7 +59,7 @@ public async Task EntityAndCustomTypeInConditionalResultAsync() using (var s = OpenSession()) await ((from x in s.Query() let parent = x.Parent - //NH-3005 - Contditional on custom type + //NH-3005 - Conditional with custom type where (parent.IsChiusa ? x.CustomType : parent.CustomType) == x.CustomType select new { diff --git a/src/NHibernate.Test/NHSpecificTest/GH2673/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH2673/Entity.cs new file mode 100644 index 00000000000..dd65748ec7c --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2673/Entity.cs @@ -0,0 +1,20 @@ +using System; + +namespace NHibernate.Test.NHSpecificTest.GH2673 +{ + [Serializable] + public class Resource + { + public virtual int Id { get; set; } + public virtual Resource Manager { get; set; } + public virtual string Name { get; set; } + public virtual Role ResourceRole { get; set; } + } + + [Serializable] + public class Role + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs new file mode 100644 index 00000000000..abccc3d0673 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs @@ -0,0 +1,134 @@ +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH2673 +{ + [TestFixture(true)] + [TestFixture(false)] + public class Fixture : TestCaseMappingByCode + { + private readonly bool _withLazyProperties; + + public Fixture(bool withLazyProperties) + { + _withLazyProperties = withLazyProperties; + } + + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var role1 = new Role {Id = 1, Name = "role1"}; + session.Save(role1); + var role2 = new Role {Id = 2, Name = "role2"}; + session.Save(role2); + + var r1 = new Resource() {Id = 1, Name = "r1", ResourceRole = role1}; + session.Save(r1); + + var r2 = new Resource() {Id = 2, Name = "r2", ResourceRole = role2}; + session.Save(r2); + + var r3 = new Resource() {Id = 3, Name = "r3", ResourceRole = role2}; + session.Save(r3); + + r1.Manager = r2; + r2.Manager = r3; + r3.Manager = r1; + transaction.Commit(); + } + } + + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.CreateQuery("delete from System.Object").ExecuteUpdate(); + + transaction.Commit(); + } + } + + [Test] + public void DeserializeSameTypeAssociationWithInitializedProxyAndCircularReferences() + { + using (var session = OpenSession()) + { + var r1 = session.Load(1); + var r2 = session.Load(2); + var r3 = session.Load(3); + + var list = session.QueryOver() + .Fetch(SelectMode.Fetch, res => res.Manager) + .List(); + + try + { + var serialised = SpoofSerialization(list[0]); + SpoofSerialization(session); + } + catch (SerializationException) + { + //Lazy properties case throws due to circular references. See GH-2563 + if (!_withLazyProperties) + throw; + } + } + } + + [Test] + public void DeserializeSameTypeAssociationWithInitializedAndNotInitializedProxy() + { + using (var session = OpenSession()) + { + var r1 = session.Get(1); + var r2 = session.Get(2); + var r1Name = r1.Name; + var serialised = SpoofSerialization(r1); + Assert.That(serialised.Name, Is.EqualTo("r1")); + } + } + + private T SpoofSerialization(T obj) + { + var formatter = new BinaryFormatter + { +#if !NETFX + SurrogateSelector = new NHibernate.Util.SerializationHelper.SurrogateSelector() +#endif + }; + var stream = new MemoryStream(); + formatter.Serialize(stream, obj); + + stream.Position = 0; + + return (T) formatter.Deserialize(stream); + } + + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.Class( + m => + { + m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned)); + m.Property(x => x.Name, x => x.Lazy(_withLazyProperties)); + m.ManyToOne(x => x.Manager, x => x.ForeignKey("none")); + m.ManyToOne(x => x.ResourceRole, x => x.ForeignKey("none")); + }); + mapper.Class( + m => + { + m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned)); + m.Property(x => x.Name); + }); + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + } +} diff --git a/src/NHibernate/Proxy/NHibernateProxyFactoryInfo.cs b/src/NHibernate/Proxy/NHibernateProxyFactoryInfo.cs index 70f3af1e2c9..5aa9c0d1624 100644 --- a/src/NHibernate/Proxy/NHibernateProxyFactoryInfo.cs +++ b/src/NHibernate/Proxy/NHibernateProxyFactoryInfo.cs @@ -74,5 +74,10 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue(nameof(_componentIdType), _componentIdType); info.AddValue(nameof(_isClassProxy), _isClassProxy); } + + internal NHibernateProxyFactoryInfo Clone() + { + return new NHibernateProxyFactoryInfo(_entityName, _persistentClass, _interfaces, _getIdentifierMethod, _setIdentifierMethod, _componentIdType, _isClassProxy); + } } } diff --git a/src/NHibernate/Proxy/NHibernateProxyObjectReference.cs b/src/NHibernate/Proxy/NHibernateProxyObjectReference.cs index b717d7061f3..a2c3a362ff5 100644 --- a/src/NHibernate/Proxy/NHibernateProxyObjectReference.cs +++ b/src/NHibernate/Proxy/NHibernateProxyObjectReference.cs @@ -51,7 +51,8 @@ public object GetRealObject(StreamingContext context) [SecurityCritical] public void GetObjectData(SerializationInfo info, StreamingContext context) { - info.AddValue(nameof(_proxyFactoryInfo), _proxyFactoryInfo); + //Save a copy as it seems IObjectReference deserialization can't properly handle multiple objects with the same reference + info.AddValue(nameof(_proxyFactoryInfo), _proxyFactoryInfo.Clone()); info.AddValue(nameof(_identifier), _identifier); info.AddValue(nameof(_implementation), _implementation); } From d7cc8a0fd7f4316dac8fc734bb08a36ff3f770d8 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Fri, 2 Apr 2021 12:01:15 +0300 Subject: [PATCH 2/2] Fix Oracle --- src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs | 2 ++ src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs index f4f94bc8eba..911f59f0585 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2673/Fixture.cs @@ -128,6 +128,7 @@ protected override HbmMapping GetMappings() mapper.Class( m => { + m.Table("ResTable"); m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned)); m.Property(x => x.Name, x => x.Lazy(_withLazyProperties)); m.ManyToOne(x => x.Manager, x => x.ForeignKey("none")); @@ -136,6 +137,7 @@ protected override HbmMapping GetMappings() mapper.Class( m => { + m.Table("RoleTable"); m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned)); m.Property(x => x.Name); }); diff --git a/src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs index abccc3d0673..1f1410b157d 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH2673/Fixture.cs @@ -117,6 +117,7 @@ protected override HbmMapping GetMappings() mapper.Class( m => { + m.Table("ResTable"); m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned)); m.Property(x => x.Name, x => x.Lazy(_withLazyProperties)); m.ManyToOne(x => x.Manager, x => x.ForeignKey("none")); @@ -125,6 +126,7 @@ protected override HbmMapping GetMappings() mapper.Class( m => { + m.Table("RoleTable"); m.Id(x => x.Id, (i) => i.Generator(Generators.Assigned)); m.Property(x => x.Name); });