diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3539/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3539/Fixture.cs new file mode 100644 index 0000000000..342d688f1c --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3539/Fixture.cs @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using NHibernate.Cfg; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH3539 +{ + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Cfg.Environment.GenerateStatistics, "true"); + } + + protected override void OnTearDown() + { + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Delete("from Person"); + t.Commit(); + } + + base.OnTearDown(); + } + + [Test] + public async Task TestComponentAsync() + { + int personId; + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) + { + var person = new Person(age: 20) + { + CardInfo = new("card1", "card2") + }; + await (s.SaveAsync(person)); + await (t.CommitAsync()); + await (s.FlushAsync()); + personId = person.Id; + } + + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) + { + var restored = await (s.GetAsync(personId)); + + var oldCardInfo = restored.CardInfo; + + Assert.That(oldCardInfo.GetCardsCopy().Contains("card1")); + Assert.That(oldCardInfo.GetCardsCopy().Contains("card2")); + + var newCardInfo = new CardInfo("card1", "card2"); + + Assert.That(oldCardInfo.GetHashCode(), Is.EqualTo(newCardInfo.GetHashCode())); + Assert.That(oldCardInfo.Equals(newCardInfo)); + + restored.CardInfo = newCardInfo; + + // Expected behaviour: + // + // At this point there should be no DML statements because newCardInfo + // is the same as the old one. But NHibernate will generate DELETE + // followed by 2 INSERT into OldCards table. + + using (var x = new SqlLogSpy()) + { + await (t.CommitAsync()); + Assert.That(x.GetWholeLog(), Is.Empty); + } + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3539/CardInfo.cs b/src/NHibernate.Test/NHSpecificTest/GH3539/CardInfo.cs new file mode 100644 index 0000000000..9596e2b61e --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3539/CardInfo.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace NHibernate.Test.NHSpecificTest.GH3539; + +public class CardInfo +{ + private readonly ISet _oldCards; + + protected CardInfo() { } + + public CardInfo(params string[] cards) + { + _oldCards = cards.ToHashSet(); + } + + public virtual ISet GetCardsCopy() + { + return _oldCards.ToHashSet(); + } + + public override bool Equals(object obj) + { + if (obj is null) + { + return false; + } + if (ReferenceEquals(this, obj)) + { + return true; + } + if (obj.GetType() != GetType()) + { + return false; + } + var other = (CardInfo) obj; + return _oldCards.SetEquals(other._oldCards); + } + + public override int GetHashCode() + { + var hashCode = new HashCode(); + foreach (var card in _oldCards) hashCode.Add(card); + return hashCode.ToHashCode(); + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3539/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3539/Fixture.cs new file mode 100644 index 0000000000..2311b2e50e --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3539/Fixture.cs @@ -0,0 +1,74 @@ +using NHibernate.Cfg; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH3539 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Cfg.Environment.GenerateStatistics, "true"); + } + + protected override void OnTearDown() + { + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) + { + s.Delete("from Person"); + t.Commit(); + } + + base.OnTearDown(); + } + + [Test] + public void TestComponent() + { + int personId; + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) + { + var person = new Person(age: 20) + { + CardInfo = new("card1", "card2") + }; + s.Save(person); + t.Commit(); + s.Flush(); + personId = person.Id; + } + + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) + { + var restored = s.Get(personId); + + var oldCardInfo = restored.CardInfo; + + Assert.That(oldCardInfo.GetCardsCopy().Contains("card1")); + Assert.That(oldCardInfo.GetCardsCopy().Contains("card2")); + + var newCardInfo = new CardInfo("card1", "card2"); + + Assert.That(oldCardInfo.GetHashCode(), Is.EqualTo(newCardInfo.GetHashCode())); + Assert.That(oldCardInfo.Equals(newCardInfo)); + + restored.CardInfo = newCardInfo; + + // Expected behaviour: + // + // At this point there should be no DML statements because newCardInfo + // is the same as the old one. But NHibernate will generate DELETE + // followed by 2 INSERT into OldCards table. + + using (var x = new SqlLogSpy()) + { + t.Commit(); + Assert.That(x.GetWholeLog(), Is.Empty); + } + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3539/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH3539/Mappings.hbm.xml new file mode 100644 index 0000000000..a153459cc0 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3539/Mappings.hbm.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/NHSpecificTest/GH3539/Person.cs b/src/NHibernate.Test/NHSpecificTest/GH3539/Person.cs new file mode 100644 index 0000000000..ce4fe44848 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3539/Person.cs @@ -0,0 +1,35 @@ +namespace NHibernate.Test.NHSpecificTest.GH3539 +{ + public class Person + { + private int _id; + private int _age; + private CardInfo _cardInfo; + + protected Person() { } + + public Person(int age) + { + _cardInfo = new(); + _age = age; + } + + public virtual int Id + { + get { return _id; } + set { _id = value; } + } + + public virtual int Age + { + get { return _age; } + set { _age = value; } + } + + public virtual CardInfo CardInfo + { + get { return _cardInfo; } + set { _cardInfo = value; } + } + } +}