From e6df6f411ee5424f868b61cbf9b6d6016a559814 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Fri, 9 Oct 2020 17:30:39 -0400 Subject: [PATCH 01/18] GH2552 Add basic OneToOneType persistence test. --- src/NHibernate.Test/OneToOneType/Details.cs | 9 +++ src/NHibernate.Test/OneToOneType/Fixture.cs | 61 +++++++++++++++++++ .../OneToOneType/Mappings.hbm.xml | 18 ++++++ src/NHibernate.Test/OneToOneType/Owner.cs | 26 ++++++++ 4 files changed, 114 insertions(+) create mode 100644 src/NHibernate.Test/OneToOneType/Details.cs create mode 100644 src/NHibernate.Test/OneToOneType/Fixture.cs create mode 100644 src/NHibernate.Test/OneToOneType/Mappings.hbm.xml create mode 100644 src/NHibernate.Test/OneToOneType/Owner.cs diff --git a/src/NHibernate.Test/OneToOneType/Details.cs b/src/NHibernate.Test/OneToOneType/Details.cs new file mode 100644 index 00000000000..0ffb4fc799e --- /dev/null +++ b/src/NHibernate.Test/OneToOneType/Details.cs @@ -0,0 +1,9 @@ +namespace NHibernate.Test.OneToOneType +{ + public class Details + { + public virtual int Id { get; protected set; } + public virtual Owner Owner { get; protected internal set; } + public virtual string Data { get; set; } + } +} diff --git a/src/NHibernate.Test/OneToOneType/Fixture.cs b/src/NHibernate.Test/OneToOneType/Fixture.cs new file mode 100644 index 00000000000..52c363eb167 --- /dev/null +++ b/src/NHibernate.Test/OneToOneType/Fixture.cs @@ -0,0 +1,61 @@ +using NHibernate.Test.NHSpecificTest; +using NUnit.Framework; + +namespace NHibernate.Test.OneToOneType +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override void OnTearDown() + { + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateQuery("delete from Details").ExecuteUpdate(); + s.CreateQuery("delete from Owner").ExecuteUpdate(); + + tx.Commit(); + } + } + + [Test] + public void OneToOnePersistedOnOwnerUpdate() + { + object ownerId; + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + var owner = new Owner() + { + Name = "Owner", + }; + + ownerId = s.Save(owner); + + tx.Commit(); + } + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + Owner owner = s.Load(ownerId); + + owner.Details = new Details() + { + Data = "Owner Details" + }; + + tx.Commit(); + } + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + Owner owner = s.Get(ownerId); + + Assert.NotNull(owner.Details); + } + } + } +} diff --git a/src/NHibernate.Test/OneToOneType/Mappings.hbm.xml b/src/NHibernate.Test/OneToOneType/Mappings.hbm.xml new file mode 100644 index 00000000000..00ba8e5fe9b --- /dev/null +++ b/src/NHibernate.Test/OneToOneType/Mappings.hbm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + Owner + + + + + + diff --git a/src/NHibernate.Test/OneToOneType/Owner.cs b/src/NHibernate.Test/OneToOneType/Owner.cs new file mode 100644 index 00000000000..d810f1fb4e6 --- /dev/null +++ b/src/NHibernate.Test/OneToOneType/Owner.cs @@ -0,0 +1,26 @@ +namespace NHibernate.Test.OneToOneType +{ + public class Owner + { + private Details _details; + + public virtual int Id { get; protected set; } + public virtual string Name { get; set; } + public virtual Details Details + { + get + { + return _details; + } + set + { + _details = value; + + if (_details != null) + { + _details.Owner = this; + } + } + } + } +} From e5e54b23e03a6b847e48cf745bf1cbe160c1f1e1 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Wed, 14 Oct 2020 17:48:18 -0400 Subject: [PATCH 02/18] GH2552 Add basic async OneToOneType persistence test. --- .../Async/OneToOneType/Fixture.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/NHibernate.Test/Async/OneToOneType/Fixture.cs diff --git a/src/NHibernate.Test/Async/OneToOneType/Fixture.cs b/src/NHibernate.Test/Async/OneToOneType/Fixture.cs new file mode 100644 index 00000000000..02da8d4dae9 --- /dev/null +++ b/src/NHibernate.Test/Async/OneToOneType/Fixture.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// 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.Test.NHSpecificTest; +using NUnit.Framework; + +namespace NHibernate.Test.OneToOneType +{ + using System.Threading.Tasks; + [TestFixture] + public class FixtureAsync : BugTestCase + { + protected override void OnTearDown() + { + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateQuery("delete from Details").ExecuteUpdate(); + s.CreateQuery("delete from Owner").ExecuteUpdate(); + + tx.Commit(); + } + } + + [Test] + public async Task OneToOnePersistedOnOwnerUpdateAsync() + { + object ownerId; + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + var owner = new Owner() + { + Name = "Owner", + }; + + ownerId = await (s.SaveAsync(owner)); + + await (tx.CommitAsync()); + } + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + Owner owner = await (s.LoadAsync(ownerId)); + + owner.Details = new Details() + { + Data = "Owner Details" + }; + + await (tx.CommitAsync()); + } + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + Owner owner = await (s.GetAsync(ownerId)); + + Assert.NotNull(owner.Details); + } + } + } +} From b639fc588851d757970d111b14f1e4570902f53b Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Fri, 2 Oct 2020 12:25:44 -0400 Subject: [PATCH 03/18] GH2552 Add tests for GitHub issue #2552 --- .../NHSpecificTest/GH2552/Details.cs | 11 ++ .../NHSpecificTest/GH2552/Fixture.cs | 129 ++++++++++++++++++ .../NHSpecificTest/GH2552/Mappings.hbm.xml | 34 +++++ .../NHSpecificTest/GH2552/Person.cs | 29 ++++ 4 files changed, 203 insertions(+) create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2552/Details.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2552/Mappings.hbm.xml create mode 100644 src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Details.cs b/src/NHibernate.Test/NHSpecificTest/GH2552/Details.cs new file mode 100644 index 00000000000..103cfa1db00 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Details.cs @@ -0,0 +1,11 @@ +namespace NHibernate.Test.NHSpecificTest.GH2552 +{ + public abstract class Details + { + public virtual int Id { get; protected set; } + public virtual Person Person { get; set; } + public virtual string Data { get; set; } + } + public class DetailsByFK : Details { } + public class DetailsByRef : Details { } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs new file mode 100644 index 00000000000..678afa6b7f0 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using NHibernate.Cache; +using NHibernate.Stat; +using NHibernate.Test.CacheTest.Caches; +using NUnit.Framework; +using NHCfg = NHibernate.Cfg; + +namespace NHibernate.Test.NHSpecificTest.GH2552 +{ + [TestFixture] + public class Fixture : BugTestCase + { + protected override string CacheConcurrencyStrategy => null; + + protected override void Configure(NHCfg.Configuration configuration) + { + configuration.SetProperty(NHCfg.Environment.UseSecondLevelCache, "true"); + configuration.SetProperty(NHCfg.Environment.GenerateStatistics, "true"); + } + + protected override void OnTearDown() + { + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateQuery("delete from DetailsByFK").ExecuteUpdate(); + s.CreateQuery("delete from PersonByFK").ExecuteUpdate(); + s.CreateQuery("delete from DetailsByRef").ExecuteUpdate(); + s.CreateQuery("delete from PersonByRef").ExecuteUpdate(); + + tx.Commit(); + } + + Sfi.Evict(typeof(PersonByFK)); + Sfi.Evict(typeof(DetailsByFK)); + Sfi.Evict(typeof(PersonByRef)); + Sfi.Evict(typeof(DetailsByRef)); + } + + private void OneToOneTest() where TPerson : Person, new() where TDetails : Details, new() + { + List ids = this.CreatePersonAndDetails(); + + IStatistics statistics = Sfi.Statistics; + + // Clear the second level cache and the statistics + Sfi.EvictEntity(typeof(TPerson).FullName); + Sfi.EvictEntity(typeof(TDetails).FullName); + Sfi.EvictQueries(); + + statistics.Clear(); + + // Fill the empty caches with data. + this.FetchPeopleById(ids); + + // Verify that no data was retrieved from the cache. + Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count"); + + statistics.Clear(); + + this.FetchPeopleById(ids); + + Assert.AreEqual(0, statistics.SecondLevelCacheMissCount, "Second level cache miss count"); + } + + private List CreatePersonAndDetails() where TPerson : Person, new() where TDetails : Details, new() + { + List ids = new List(); + + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + for (int i = 0; i < 6; i++) + { + Person person = new TPerson(); + + if (i % 2 == 0) + { + Details details = new TDetails(); + + details.Data = String.Format("{0}{1}", typeof(TDetails).Name, i); + + person.Details = details; + } + + person.Name = String.Format("{0}{1}", typeof(TPerson).Name, i); + + ids.Add(s.Save(person)); + } + + tx.Commit(); + } + + return ids; + } + + public IList FetchPeopleById(List ids) where TPerson : Person + { + IList people = new List(); + + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + foreach (object id in ids) + { + people.Add(s.Get(id)); + } + + tx.Commit(); + } + + return people; + } + + [Test] + public void OneToOneCacheByForeignKey() + { + OneToOneTest(); + } + + [Test] + public void OneToOneCacheByRef() + { + OneToOneTest(); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH2552/Mappings.hbm.xml new file mode 100644 index 00000000000..47d465a0255 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Mappings.hbm.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + Person + + + + + + + + + + + + + + + + + + + + diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs b/src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs new file mode 100644 index 00000000000..14a12c209e7 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs @@ -0,0 +1,29 @@ +namespace NHibernate.Test.NHSpecificTest.GH2552 +{ + public abstract class Person + { + private Details _details; + + public virtual int Id { get; protected set; } + public virtual string Name { get; set; } + public virtual Details Details { + get + { + return _details; + } + set + { + _details = value; + + if (_details != null) + { + _details.Person = this; + } + } + } + + } + public class PersonByFK : Person { } + + public class PersonByRef : Person { } +} From 6e73422e53b17cdd1ac9d93c77a684f2142b3ef9 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Mon, 5 Oct 2020 11:56:57 -0400 Subject: [PATCH 04/18] GH2552 Add async tests for GitHub issue #2552 --- .../Async/NHSpecificTest/GH2552/Fixture.cs | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs new file mode 100644 index 00000000000..dd630e2382a --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// +// 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; +using System.Collections.Generic; +using System.Reflection; +using NHibernate.Cache; +using NHibernate.Stat; +using NHibernate.Test.CacheTest.Caches; +using NUnit.Framework; +using NHCfg = NHibernate.Cfg; + +namespace NHibernate.Test.NHSpecificTest.GH2552 +{ + using System.Threading.Tasks; + using System.Threading; + [TestFixture] + public class FixtureAsync : BugTestCase + { + protected override string CacheConcurrencyStrategy => null; + + protected override void Configure(NHCfg.Configuration configuration) + { + configuration.SetProperty(NHCfg.Environment.UseSecondLevelCache, "true"); + configuration.SetProperty(NHCfg.Environment.GenerateStatistics, "true"); + } + + protected override void OnTearDown() + { + using (var s = OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.CreateQuery("delete from DetailsByFK").ExecuteUpdate(); + s.CreateQuery("delete from PersonByFK").ExecuteUpdate(); + s.CreateQuery("delete from DetailsByRef").ExecuteUpdate(); + s.CreateQuery("delete from PersonByRef").ExecuteUpdate(); + + tx.Commit(); + } + + Sfi.Evict(typeof(PersonByFK)); + Sfi.Evict(typeof(DetailsByFK)); + Sfi.Evict(typeof(PersonByRef)); + Sfi.Evict(typeof(DetailsByRef)); + } + + private async Task OneToOneTestAsync(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new() + { + List ids = await (this.CreatePersonAndDetailsAsync(cancellationToken)); + + IStatistics statistics = Sfi.Statistics; + + // Clear the second level cache and the statistics + await (Sfi.EvictEntityAsync(typeof(TPerson).FullName, cancellationToken)); + await (Sfi.EvictEntityAsync(typeof(TDetails).FullName, cancellationToken)); + await (Sfi.EvictQueriesAsync(cancellationToken)); + + statistics.Clear(); + + // Fill the empty caches with data. + await (this.FetchPeopleByIdAsync(ids, cancellationToken)); + + // Verify that no data was retrieved from the cache. + Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count"); + + statistics.Clear(); + + await (this.FetchPeopleByIdAsync(ids, cancellationToken)); + + Assert.AreEqual(0, statistics.SecondLevelCacheMissCount, "Second level cache miss count"); + } + + private async Task> CreatePersonAndDetailsAsync(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new() + { + List ids = new List(); + + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + for (int i = 0; i < 6; i++) + { + Person person = new TPerson(); + + if (i % 2 == 0) + { + Details details = new TDetails(); + + details.Data = String.Format("{0}{1}", typeof(TDetails).Name, i); + + person.Details = details; + } + + person.Name = String.Format("{0}{1}", typeof(TPerson).Name, i); + + ids.Add(await (s.SaveAsync(person, cancellationToken))); + } + + await (tx.CommitAsync(cancellationToken)); + } + + return ids; + } + + public async Task> FetchPeopleByIdAsync(List ids, CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person + { + IList people = new List(); + + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + foreach (object id in ids) + { + people.Add(await (s.GetAsync(id, cancellationToken))); + } + + await (tx.CommitAsync(cancellationToken)); + } + + return people; + } + + [Test] + public async Task OneToOneCacheByForeignKeyAsync() + { + await (OneToOneTestAsync()); + } + + [Test] + public async Task OneToOneCacheByRefAsync() + { + await (OneToOneTestAsync()); + } + } +} From 957740d310627b7f0b1bace731f6d8892794df29 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Thu, 1 Oct 2020 17:39:32 -0400 Subject: [PATCH 05/18] GH2552 Fix caching of OneToOneType from second level cache. --- src/NHibernate/Type/OneToOneType.cs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/NHibernate/Type/OneToOneType.cs b/src/NHibernate/Type/OneToOneType.cs index 0837ea3b98a..4763df6c087 100644 --- a/src/NHibernate/Type/OneToOneType.cs +++ b/src/NHibernate/Type/OneToOneType.cs @@ -135,15 +135,32 @@ public override bool UseLHSPrimaryKey public override object Disassemble(object value, ISessionImplementor session, object owner) { - return null; + if (value == null) + { + return null; + } + + object id = ForeignKeys.GetEntityIdentifierIfNotUnsaved(GetAssociatedEntityName(), value, session); + + if (id == null) + { + throw new AssertionFailure("cannot cache a reference to an object with a null id: " + GetAssociatedEntityName()); + } + + return GetIdentifierType(session).Disassemble(id, session, owner); } public override object Assemble(object cached, ISessionImplementor session, object owner) { - //this should be a call to resolve(), not resolveIdentifier(), - //'cos it might be a property-ref, and we did not cache the - //referenced value - return ResolveIdentifier(session.GetContextEntityIdentifier(owner), session, owner); + // the owner of the association is not the owner of the id + object id = GetIdentifierType(session).Assemble(cached, session, null); + + if (id == null) + { + return null; + } + + return ResolveIdentifier(id, session); } /// From f9477226194b9707cf0a8981a386b0d70b25bf82 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Mon, 5 Oct 2020 12:12:45 -0400 Subject: [PATCH 06/18] GH2552 Fix async caching of OneToOneType from second level cache. --- src/NHibernate/Async/Type/OneToOneType.cs | 43 +++++++++++------------ 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/NHibernate/Async/Type/OneToOneType.cs b/src/NHibernate/Async/Type/OneToOneType.cs index d53f9c968d2..17a61f3c6c8 100644 --- a/src/NHibernate/Async/Type/OneToOneType.cs +++ b/src/NHibernate/Async/Type/OneToOneType.cs @@ -123,39 +123,36 @@ public override async Task HydrateAsync(DbDataReader rs, string[] names, return identifier; } - public override Task DisassembleAsync(object value, ISessionImplementor session, object owner, CancellationToken cancellationToken) + public override async Task DisassembleAsync(object value, ISessionImplementor session, object owner, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - try + cancellationToken.ThrowIfCancellationRequested(); + if (value == null) { - return Task.FromResult(Disassemble(value, session, owner)); + return null; } - catch (Exception ex) + + object id = await (ForeignKeys.GetEntityIdentifierIfNotUnsavedAsync(GetAssociatedEntityName(), value, session, cancellationToken)).ConfigureAwait(false); + + if (id == null) { - return Task.FromException(ex); + throw new AssertionFailure("cannot cache a reference to an object with a null id: " + GetAssociatedEntityName()); } + + return await (GetIdentifierType(session).DisassembleAsync(id, session, owner, cancellationToken)).ConfigureAwait(false); } - public override Task AssembleAsync(object cached, ISessionImplementor session, object owner, CancellationToken cancellationToken) + public override async Task AssembleAsync(object cached, ISessionImplementor session, object owner, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - try - { - //this should be a call to resolve(), not resolveIdentifier(), - //'cos it might be a property-ref, and we did not cache the - //referenced value - return ResolveIdentifierAsync(session.GetContextEntityIdentifier(owner), session, owner, cancellationToken); - } - catch (Exception ex) + cancellationToken.ThrowIfCancellationRequested(); + // the owner of the association is not the owner of the id + object id = await (GetIdentifierType(session).AssembleAsync(cached, session, null, cancellationToken)).ConfigureAwait(false); + + if (id == null) { - return Task.FromException(ex); + return null; } + + return await (ResolveIdentifierAsync(id, session, cancellationToken)).ConfigureAwait(false); } } } From d428898459cca410f93c92a3a2237b5bc1d7eec3 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Wed, 14 Oct 2020 10:08:33 -0400 Subject: [PATCH 07/18] GH2552 Implement isDirty for OneToOneType and always check if it is dirty or not. --- src/NHibernate/Type/OneToOneType.cs | 30 +++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/NHibernate/Type/OneToOneType.cs b/src/NHibernate/Type/OneToOneType.cs index 4763df6c087..57bba18b382 100644 --- a/src/NHibernate/Type/OneToOneType.cs +++ b/src/NHibernate/Type/OneToOneType.cs @@ -59,12 +59,31 @@ public override bool IsOneToOne public override bool IsDirty(object old, object current, ISessionImplementor session) { - return false; + if (IsSame(old, current)) + { + return false; + } + + if (old == null || current == null) + { + return true; + } + + if (ForeignKeys.IsTransientFast(GetAssociatedEntityName(), current, session).GetValueOrDefault()) + { + return true; + } + + object oldId = GetIdentifier(old, session); + object newId = GetIdentifier(current, session); + IType identifierType = GetIdentifierType(session); + + return identifierType.IsDirty(oldId, newId, session); } public override bool IsDirty(object old, object current, bool[] checkable, ISessionImplementor session) { - return false; + return this.IsDirty(old, current, session); } public override bool IsModified(object old, object current, bool[] checkable, ISessionImplementor session) @@ -164,13 +183,12 @@ public override object Assemble(object cached, ISessionImplementor session, obje } /// - /// We don't need to dirty check one-to-one because of how - /// assemble/disassemble is implemented and because a one-to-one - /// association is never dirty + /// We always need to dirty check as our identifier is dependent on + /// whether or not a persistent entry exists. /// public override bool IsAlwaysDirtyChecked { - get { return false; } //TODO: this is kinda inconsistent with CollectionType + get { return true; } } public override string PropertyName From 137090fda55d77141b5006377f3ff4206ec86f86 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Wed, 14 Oct 2020 17:46:42 -0400 Subject: [PATCH 08/18] GH2552 Implement async version of isDirty for OneToOneType. --- src/NHibernate/Async/Type/OneToOneType.cs | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/NHibernate/Async/Type/OneToOneType.cs b/src/NHibernate/Async/Type/OneToOneType.cs index 17a61f3c6c8..cdd57a28787 100644 --- a/src/NHibernate/Async/Type/OneToOneType.cs +++ b/src/NHibernate/Async/Type/OneToOneType.cs @@ -46,20 +46,29 @@ public override async Task NullSafeSetAsync(DbCommand cmd, object value, int ind .NullSafeSetAsync(cmd, await (GetReferenceValueAsync(value, session, cancellationToken)).ConfigureAwait(false), index, session, cancellationToken)).ConfigureAwait(false); } - public override Task IsDirtyAsync(object old, object current, ISessionImplementor session, CancellationToken cancellationToken) + public override async Task IsDirtyAsync(object old, object current, ISessionImplementor session, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) + cancellationToken.ThrowIfCancellationRequested(); + if (IsSame(old, current)) { - return Task.FromCanceled(cancellationToken); + return false; } - try + + if (old == null || current == null) { - return Task.FromResult(IsDirty(old, current, session)); + return true; } - catch (Exception ex) + + if ((await (ForeignKeys.IsTransientFastAsync(GetAssociatedEntityName(), current, session, cancellationToken)).ConfigureAwait(false)).GetValueOrDefault()) { - return Task.FromException(ex); + return true; } + + object oldId = await (GetIdentifierAsync(old, session, cancellationToken)).ConfigureAwait(false); + object newId = await (GetIdentifierAsync(current, session, cancellationToken)).ConfigureAwait(false); + IType identifierType = GetIdentifierType(session); + + return await (identifierType.IsDirtyAsync(oldId, newId, session, cancellationToken)).ConfigureAwait(false); } public override Task IsDirtyAsync(object old, object current, bool[] checkable, ISessionImplementor session, CancellationToken cancellationToken) @@ -68,14 +77,7 @@ public override Task IsDirtyAsync(object old, object current, bool[] check { return Task.FromCanceled(cancellationToken); } - try - { - return Task.FromResult(IsDirty(old, current, checkable, session)); - } - catch (Exception ex) - { - return Task.FromException(ex); - } + return this.IsDirtyAsync(old, current, session, cancellationToken); } public override Task IsModifiedAsync(object old, object current, bool[] checkable, ISessionImplementor session, CancellationToken cancellationToken) From 04b140afed5a4b5230cc22b919c4408779dd0d36 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Fri, 13 Nov 2020 02:01:40 +0200 Subject: [PATCH 09/18] Test case for session.Update --- src/NHibernate.Test/OneToOneType/Fixture.cs | 42 +++++++++++++++++++ .../OneToOneType/Mappings.hbm.xml | 4 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/NHibernate.Test/OneToOneType/Fixture.cs b/src/NHibernate.Test/OneToOneType/Fixture.cs index 52c363eb167..7bfffee851f 100644 --- a/src/NHibernate.Test/OneToOneType/Fixture.cs +++ b/src/NHibernate.Test/OneToOneType/Fixture.cs @@ -57,5 +57,47 @@ public void OneToOnePersistedOnOwnerUpdate() Assert.NotNull(owner.Details); } } + + [Test] + public void OneToOnePersistedOnOwnerUpdateForSessionUpdate() + { + Owner owner; + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + owner = new Owner() + { + Name = "Owner", + }; + + s.Save(owner); + tx.Commit(); + } + + using (var s = Sfi.OpenSession()) + { + owner = s.Get(owner.Id); + } + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + s.SaveOrUpdate(owner); + owner.Details = new Details() + { + Data = "Owner Details" + }; + + tx.Commit(); + } + + using (var s = Sfi.OpenSession()) + { + owner = s.Get(owner.Id); + + Assert.IsNotNull(owner.Details); + } + } } } diff --git a/src/NHibernate.Test/OneToOneType/Mappings.hbm.xml b/src/NHibernate.Test/OneToOneType/Mappings.hbm.xml index 00ba8e5fe9b..779ebae85fd 100644 --- a/src/NHibernate.Test/OneToOneType/Mappings.hbm.xml +++ b/src/NHibernate.Test/OneToOneType/Mappings.hbm.xml @@ -1,9 +1,9 @@  - + - + From 964faa7c14e92c3edd392755c7a258dee3d95211 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Tue, 17 Nov 2020 14:38:40 -0500 Subject: [PATCH 10/18] GH2552 Async test for one-to-one session update. --- .../Async/OneToOneType/Fixture.cs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/NHibernate.Test/Async/OneToOneType/Fixture.cs b/src/NHibernate.Test/Async/OneToOneType/Fixture.cs index 02da8d4dae9..17c8d4ae697 100644 --- a/src/NHibernate.Test/Async/OneToOneType/Fixture.cs +++ b/src/NHibernate.Test/Async/OneToOneType/Fixture.cs @@ -68,5 +68,47 @@ public async Task OneToOnePersistedOnOwnerUpdateAsync() Assert.NotNull(owner.Details); } } + + [Test] + public async Task OneToOnePersistedOnOwnerUpdateForSessionUpdateAsync() + { + Owner owner; + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + owner = new Owner() + { + Name = "Owner", + }; + + await (s.SaveAsync(owner)); + await (tx.CommitAsync()); + } + + using (var s = Sfi.OpenSession()) + { + owner = await (s.GetAsync(owner.Id)); + } + + using (var s = Sfi.OpenSession()) + using (var tx = s.BeginTransaction()) + { + await (s.SaveOrUpdateAsync(owner)); + owner.Details = new Details() + { + Data = "Owner Details" + }; + + await (tx.CommitAsync()); + } + + using (var s = Sfi.OpenSession()) + { + owner = await (s.GetAsync(owner.Id)); + + Assert.IsNotNull(owner.Details); + } + } } } From b327a472ed2caf0474bd493dedd0080b22b52a07 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Tue, 17 Nov 2020 14:40:33 -0500 Subject: [PATCH 11/18] GH2552 Implement OneToOneType.IsModified based on the ManyToOneType. --- src/NHibernate/Type/OneToOneType.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/NHibernate/Type/OneToOneType.cs b/src/NHibernate/Type/OneToOneType.cs index 57bba18b382..fce88d41976 100644 --- a/src/NHibernate/Type/OneToOneType.cs +++ b/src/NHibernate/Type/OneToOneType.cs @@ -88,7 +88,28 @@ public override bool IsDirty(object old, object current, bool[] checkable, ISess public override bool IsModified(object old, object current, bool[] checkable, ISessionImplementor session) { - return false; + if (current == null) + { + return old != null; + } + if (old == null) + { + return true; + } + var oldIdentifier = IsIdentifier(old, session) ? old : GetIdentifier(old, session); + var currentIdentifier = GetIdentifier(current, session); + // the ids are fully resolved, so compare them with isDirty(), not isModified() + return GetIdentifierOrUniqueKeyType(session.Factory).IsDirty(oldIdentifier, currentIdentifier, session); + } + + private bool IsIdentifier(object value, ISessionImplementor session) + { + var identifierType = GetIdentifierType(session); + if (identifierType == null) + { + return false; + } + return value.GetType() == identifierType.ReturnedClass; } public override bool IsNull(object owner, ISessionImplementor session) From e2b56ed610eee19817172a5dc26395706f9ce400 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Tue, 17 Nov 2020 14:42:17 -0500 Subject: [PATCH 12/18] GH2552 Update acsync version of OneToOneType.IsModified. --- src/NHibernate/Async/Type/OneToOneType.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/NHibernate/Async/Type/OneToOneType.cs b/src/NHibernate/Async/Type/OneToOneType.cs index cdd57a28787..4bf56e41117 100644 --- a/src/NHibernate/Async/Type/OneToOneType.cs +++ b/src/NHibernate/Async/Type/OneToOneType.cs @@ -80,20 +80,21 @@ public override Task IsDirtyAsync(object old, object current, bool[] check return this.IsDirtyAsync(old, current, session, cancellationToken); } - public override Task IsModifiedAsync(object old, object current, bool[] checkable, ISessionImplementor session, CancellationToken cancellationToken) + public override async Task IsModifiedAsync(object old, object current, bool[] checkable, ISessionImplementor session, CancellationToken cancellationToken) { - if (cancellationToken.IsCancellationRequested) - { - return Task.FromCanceled(cancellationToken); - } - try + cancellationToken.ThrowIfCancellationRequested(); + if (current == null) { - return Task.FromResult(IsModified(old, current, checkable, session)); + return old != null; } - catch (Exception ex) + if (old == null) { - return Task.FromException(ex); + return true; } + var oldIdentifier = IsIdentifier(old, session) ? old : await (GetIdentifierAsync(old, session, cancellationToken)).ConfigureAwait(false); + var currentIdentifier = await (GetIdentifierAsync(current, session, cancellationToken)).ConfigureAwait(false); + // the ids are fully resolved, so compare them with isDirty(), not isModified() + return await (GetIdentifierOrUniqueKeyType(session.Factory).IsDirtyAsync(oldIdentifier, currentIdentifier, session, cancellationToken)).ConfigureAwait(false); } public override async Task HydrateAsync(DbDataReader rs, string[] names, ISessionImplementor session, object owner, CancellationToken cancellationToken) From 551d170087ed03bed4c5b8138148a3d998cc56a2 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Tue, 17 Nov 2020 14:43:28 -0500 Subject: [PATCH 13/18] GH2552 Only check if the OneToOneType is dirty when it is nullable. --- src/NHibernate/Type/OneToOneType.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/NHibernate/Type/OneToOneType.cs b/src/NHibernate/Type/OneToOneType.cs index fce88d41976..1400dacc57b 100644 --- a/src/NHibernate/Type/OneToOneType.cs +++ b/src/NHibernate/Type/OneToOneType.cs @@ -204,12 +204,11 @@ public override object Assemble(object cached, ISessionImplementor session, obje } /// - /// We always need to dirty check as our identifier is dependent on - /// whether or not a persistent entry exists. + /// We only need to dirty check when the identifier can be null. /// public override bool IsAlwaysDirtyChecked { - get { return true; } + get { return IsNullable; } } public override string PropertyName From 9e6944a97300184740d1ee5f0cbbef4bf1fe45a9 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Tue, 17 Nov 2020 15:08:07 -0500 Subject: [PATCH 14/18] GH2552 Rename tests to identifiy their purpose of testing fetching data from the cache. --- .../Async/NHSpecificTest/GH2552/Fixture.cs | 10 +++++----- src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs index dd630e2382a..b7ab7de6def 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs @@ -51,7 +51,7 @@ protected override void OnTearDown() Sfi.Evict(typeof(DetailsByRef)); } - private async Task OneToOneTestAsync(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new() + private async Task OneToOneFetchTestAsync(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new() { List ids = await (this.CreatePersonAndDetailsAsync(cancellationToken)); @@ -127,15 +127,15 @@ protected override void OnTearDown() } [Test] - public async Task OneToOneCacheByForeignKeyAsync() + public async Task OneToOneCacheFetchByForeignKeyAsync() { - await (OneToOneTestAsync()); + await (OneToOneFetchTestAsync()); } [Test] - public async Task OneToOneCacheByRefAsync() + public async Task OneToOneCacheFetchByRefAsync() { - await (OneToOneTestAsync()); + await (OneToOneFetchTestAsync()); } } } diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs index 678afa6b7f0..25e46a89e88 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs @@ -39,7 +39,7 @@ protected override void OnTearDown() Sfi.Evict(typeof(DetailsByRef)); } - private void OneToOneTest() where TPerson : Person, new() where TDetails : Details, new() + private void OneToOneFetchTest() where TPerson : Person, new() where TDetails : Details, new() { List ids = this.CreatePersonAndDetails(); @@ -115,15 +115,15 @@ public IList FetchPeopleById(List ids) where TPerson : } [Test] - public void OneToOneCacheByForeignKey() + public void OneToOneCacheFetchByForeignKey() { - OneToOneTest(); + OneToOneFetchTest(); } [Test] - public void OneToOneCacheByRef() + public void OneToOneCacheFetchByRef() { - OneToOneTest(); + OneToOneFetchTest(); } } } From ab23e13727422066066eb9b78d7fac8eefcd5857 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Wed, 18 Nov 2020 08:46:05 -0500 Subject: [PATCH 15/18] GH2552 Add tests to verify that the cache is updated on delete. --- .../NHSpecificTest/GH2552/Fixture.cs | 85 +++++++++++++++++++ .../NHSpecificTest/GH2552/Mappings.hbm.xml | 12 +-- 2 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs index 25e46a89e88..3e9560c84ab 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs @@ -65,6 +65,79 @@ protected override void OnTearDown() Assert.AreEqual(0, statistics.SecondLevelCacheMissCount, "Second level cache miss count"); } + private void OneToOneUpdateTest() where TPerson : Person, new() where TDetails : Details, new() + { + List ids = this.CreatePersonAndDetails(); + + IStatistics statistics = Sfi.Statistics; + + // Clear the second level cache and the statistics + Sfi.EvictEntity(typeof(TPerson).FullName); + Sfi.EvictEntity(typeof(TDetails).FullName); + Sfi.EvictQueries(); + + statistics.Clear(); + + // Fill the empty caches with data. + this.FetchPeopleById(ids); + + // Verify that no data was retrieved from the cache. + Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count"); + statistics.Clear(); + + int personId = DeleteDetailsFromFirstPerson(); + + // Verify that the cache was updated + Assert.AreEqual(1, statistics.SecondLevelCachePutCount, "Second level cache put count"); + statistics.Clear(); + + // Verify that the Person was updated in the cache + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + TPerson person = s.Get(personId); + + Assert.IsNull(person.Details); + } + + Assert.AreEqual(0, statistics.SecondLevelCacheMissCount, "Second level cache miss count"); + statistics.Clear(); + + // Verify that the Details was removed from the cache and deleted. + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + TDetails details = s.Get(personId); + + Assert.Null(details); + } + + Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count"); + } + + private int DeleteDetailsFromFirstPerson() where TPerson:Person + { + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + // Get the first person with details. + Person person = s.QueryOver() + .Where(p => p.Details != null) + .Take(1) + .SingleOrDefault(); + + Assert.NotNull(person); + Assert.NotNull(person.Details); + + s.SaveOrUpdate(person); + person.Details = null; + + tx.Commit(); + + return person.Id; + } + } + private List CreatePersonAndDetails() where TPerson : Person, new() where TDetails : Details, new() { List ids = new List(); @@ -125,5 +198,17 @@ public void OneToOneCacheFetchByRef() { OneToOneFetchTest(); } + + [Test] + public void OneToOneCacheUpdateByForeignKey() + { + OneToOneUpdateTest(); + } + + [Test] + public void OneToOneCacheUpdateByRef() + { + OneToOneUpdateTest(); + } } } diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH2552/Mappings.hbm.xml index 47d465a0255..0fd9f39b0e8 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH2552/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Mappings.hbm.xml @@ -1,14 +1,14 @@  - + - + - + Person @@ -19,14 +19,14 @@ - + - + - + From 65a12ea22f76f0d6afc9fd42e5aa80de583473a1 Mon Sep 17 00:00:00 2001 From: David Ellingsworth Date: Wed, 18 Nov 2020 08:50:34 -0500 Subject: [PATCH 16/18] GH2552 Add async version of tests to verify cache updated on delete. --- .../Async/NHSpecificTest/GH2552/Fixture.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs index b7ab7de6def..3533109c59c 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs @@ -77,6 +77,79 @@ protected override void OnTearDown() Assert.AreEqual(0, statistics.SecondLevelCacheMissCount, "Second level cache miss count"); } + private async Task OneToOneUpdateTestAsync(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new() + { + List ids = await (this.CreatePersonAndDetailsAsync(cancellationToken)); + + IStatistics statistics = Sfi.Statistics; + + // Clear the second level cache and the statistics + await (Sfi.EvictEntityAsync(typeof(TPerson).FullName, cancellationToken)); + await (Sfi.EvictEntityAsync(typeof(TDetails).FullName, cancellationToken)); + await (Sfi.EvictQueriesAsync(cancellationToken)); + + statistics.Clear(); + + // Fill the empty caches with data. + await (this.FetchPeopleByIdAsync(ids, cancellationToken)); + + // Verify that no data was retrieved from the cache. + Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count"); + statistics.Clear(); + + int personId = await (DeleteDetailsFromFirstPersonAsync(cancellationToken)); + + // Verify that the cache was updated + Assert.AreEqual(1, statistics.SecondLevelCachePutCount, "Second level cache put count"); + statistics.Clear(); + + // Verify that the Person was updated in the cache + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + TPerson person = await (s.GetAsync(personId, cancellationToken)); + + Assert.IsNull(person.Details); + } + + Assert.AreEqual(0, statistics.SecondLevelCacheMissCount, "Second level cache miss count"); + statistics.Clear(); + + // Verify that the Details was removed from the cache and deleted. + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + TDetails details = await (s.GetAsync(personId, cancellationToken)); + + Assert.Null(details); + } + + Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count"); + } + + private async Task DeleteDetailsFromFirstPersonAsync(CancellationToken cancellationToken = default(CancellationToken)) where TPerson:Person + { + using (ISession s = Sfi.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + // Get the first person with details. + Person person = await (s.QueryOver() + .Where(p => p.Details != null) + .Take(1) + .SingleOrDefaultAsync(cancellationToken)); + + Assert.NotNull(person); + Assert.NotNull(person.Details); + + await (s.SaveOrUpdateAsync(person, cancellationToken)); + person.Details = null; + + await (tx.CommitAsync(cancellationToken)); + + return person.Id; + } + } + private async Task> CreatePersonAndDetailsAsync(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new() { List ids = new List(); @@ -137,5 +210,17 @@ public async Task OneToOneCacheFetchByRefAsync() { await (OneToOneFetchTestAsync()); } + + [Test] + public async Task OneToOneCacheUpdateByForeignKeyAsync() + { + await (OneToOneUpdateTestAsync()); + } + + [Test] + public async Task OneToOneCacheUpdateByRefAsync() + { + await (OneToOneUpdateTestAsync()); + } } } From 57a5ed25a05a19df3185ce8cff65c8fcfcfcaba2 Mon Sep 17 00:00:00 2001 From: Roman Artiukhin Date: Wed, 18 Nov 2020 17:55:00 +0200 Subject: [PATCH 17/18] Small test fixes --- .../Async/NHSpecificTest/GH2552/Fixture.cs | 8 +++----- src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs | 7 ++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs index 3533109c59c..ead8cfc3788 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH2552/Fixture.cs @@ -10,12 +10,11 @@ using System; using System.Collections.Generic; -using System.Reflection; -using NHibernate.Cache; +using System.Linq; using NHibernate.Stat; -using NHibernate.Test.CacheTest.Caches; using NUnit.Framework; using NHCfg = NHibernate.Cfg; +using NHibernate.Linq; namespace NHibernate.Test.NHSpecificTest.GH2552 { @@ -133,7 +132,7 @@ protected override void OnTearDown() using (ITransaction tx = s.BeginTransaction()) { // Get the first person with details. - Person person = await (s.QueryOver() + Person person = await (s.Query() .Where(p => p.Details != null) .Take(1) .SingleOrDefaultAsync(cancellationToken)); @@ -141,7 +140,6 @@ protected override void OnTearDown() Assert.NotNull(person); Assert.NotNull(person.Details); - await (s.SaveOrUpdateAsync(person, cancellationToken)); person.Details = null; await (tx.CommitAsync(cancellationToken)); diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs index 3e9560c84ab..577d0ce3a8f 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Fixture.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Reflection; -using NHibernate.Cache; +using System.Linq; using NHibernate.Stat; -using NHibernate.Test.CacheTest.Caches; using NUnit.Framework; using NHCfg = NHibernate.Cfg; @@ -121,7 +119,7 @@ private int DeleteDetailsFromFirstPerson() where TPerson:Person using (ITransaction tx = s.BeginTransaction()) { // Get the first person with details. - Person person = s.QueryOver() + Person person = s.Query() .Where(p => p.Details != null) .Take(1) .SingleOrDefault(); @@ -129,7 +127,6 @@ private int DeleteDetailsFromFirstPerson() where TPerson:Person Assert.NotNull(person); Assert.NotNull(person.Details); - s.SaveOrUpdate(person); person.Details = null; tx.Commit(); From 26e370cd7876e1c4734878a19c78985cd15b4b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Delaporte?= <12201973+fredericdelaporte@users.noreply.github.com> Date: Thu, 25 Feb 2021 17:03:49 +0100 Subject: [PATCH 18/18] Fix minor formatting issues --- src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs b/src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs index 14a12c209e7..c3d7691b7f9 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH2552/Person.cs @@ -3,14 +3,13 @@ public abstract class Person { private Details _details; - + public virtual int Id { get; protected set; } public virtual string Name { get; set; } - public virtual Details Details { - get - { - return _details; - } + + public virtual Details Details + { + get { return _details; } set { _details = value; @@ -21,8 +20,8 @@ public virtual Details Details { } } } - } + public class PersonByFK : Person { } public class PersonByRef : Person { }