diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH1994/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH1994/Fixture.cs index 89f287d5688..ff47bd2ad14 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/GH1994/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH1994/Fixture.cs @@ -117,6 +117,23 @@ public async Task TestFilteredQueryOverAsync() } } + [Test] + public async Task TestFilteredBagQueryOverAsync() + { + using (var s = OpenSession()) + { + s.EnableFilter("deletedFilter").SetParameter("deletedParam", false); + + var query = await (s.QueryOver() + .Fetch(SelectMode.Fetch, x => x.DocumentsBag) + .TransformUsing(Transformers.DistinctRootEntity) + .ListAsync()); + + Assert.That(query.Count, Is.EqualTo(1), "filtered assets"); + Assert.That(query[0].DocumentsBag.Count, Is.EqualTo(1), "filtered asset documents"); + } + } + //NH-2991 [Test] public async Task TestQueryOverRestrictionWithClauseAsync() diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH750/Fixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH750/Fixture.cs index 828332a5867..ebdf2b44c5f 100644 --- a/src/NHibernate.Test/Async/NHSpecificTest/NH750/Fixture.cs +++ b/src/NHibernate.Test/Async/NHSpecificTest/NH750/Fixture.cs @@ -9,6 +9,7 @@ using System; +using NHibernate.Cfg; using NUnit.Framework; namespace NHibernate.Test.NHSpecificTest.NH750 @@ -27,6 +28,17 @@ protected override void OnTearDown() } } + protected override string CacheConcurrencyStrategy + { + get { return null; } + } + + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Cfg.Environment.UseSecondLevelCache, "false"); + base.Configure(configuration); + } + [Test] public async Task DeviceOfDriveAsync() { @@ -66,7 +78,8 @@ public async Task DeviceOfDriveAsync() dv2 = (Device) await (s.LoadAsync(typeof(Device), dvSavedId[1])); } Assert.AreEqual(2, dv1.Drives.Count); - Assert.AreEqual(2, dv2.Drives.Count); + // Verify one is missing + Assert.AreEqual(1, dv2.Drives.Count); // Verify dv1 unchanged Assert.IsTrue(dv1.Drives.Contains(dr1)); Assert.IsTrue(dv1.Drives.Contains(dr2)); @@ -74,13 +87,43 @@ public async Task DeviceOfDriveAsync() // Verify dv2 Assert.IsTrue(dv2.Drives.Contains(dr1)); Assert.IsFalse(dv2.Drives.Contains(dr3)); - // Verify one null - int nullCount = 0; - for (int i = 0; i < dv2.Drives.Count; i++) + + //Make sure that flush didn't touch not-found="ignore" records for not modified collection + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) { - if (dv2.Drives[i] == null) nullCount++; + dv2 = await (s.GetAsync(dv2.Id)); + await (s.FlushAsync()); + await (t.CommitAsync()); + } + + await (VerifyResultAsync(expectedInCollection: 1, expectedInDb: 2, msg: "not modified collection")); + + //Many-to-many clears collection and recreates it so not-found ignore records are lost + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) + { + dv2 = await (s.GetAsync(dv2.Id)); + dv2.Drives.Add(dr2); + await (t.CommitAsync()); + } + + await (VerifyResultAsync(2,2, msg: "modified collection")); + + async Task VerifyResultAsync(int expectedInCollection, int expectedInDb, string msg) + { + using (var s = Sfi.OpenSession()) + { + var realCound = Convert.ToInt32( + await (s.CreateSQLQuery("select count(*) from DriveOfDevice where DeviceId = :id ") + .SetParameter("id", dv2.Id) + .UniqueResultAsync())); + dv2 = await (s.GetAsync(dv2.Id)); + + Assert.That(dv2.Drives.Count, Is.EqualTo(expectedInCollection), msg); + Assert.That(realCound, Is.EqualTo(expectedInDb), msg); + } } - Assert.AreEqual(1, nullCount); } } -} \ No newline at end of file +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH1994/Entity.cs b/src/NHibernate.Test/NHSpecificTest/GH1994/Entity.cs index 44f9719be0b..3d195379b5d 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH1994/Entity.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH1994/Entity.cs @@ -14,6 +14,7 @@ public class Asset : Base { public virtual ISet Documents { get; set; } = new HashSet(); public virtual ISet DocumentsFiltered { get; set; } = new HashSet(); + public virtual IList DocumentsBag { get; set; } = new List(); } public class Document : Base diff --git a/src/NHibernate.Test/NHSpecificTest/GH1994/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/GH1994/Fixture.cs index e7d07216d2d..5dbe043ebfe 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH1994/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/GH1994/Fixture.cs @@ -106,6 +106,23 @@ public void TestFilteredQueryOver() } } + [Test] + public void TestFilteredBagQueryOver() + { + using (var s = OpenSession()) + { + s.EnableFilter("deletedFilter").SetParameter("deletedParam", false); + + var query = s.QueryOver() + .Fetch(SelectMode.Fetch, x => x.DocumentsBag) + .TransformUsing(Transformers.DistinctRootEntity) + .List(); + + Assert.That(query.Count, Is.EqualTo(1), "filtered assets"); + Assert.That(query[0].DocumentsBag.Count, Is.EqualTo(1), "filtered asset documents"); + } + } + //NH-2991 [Test] public void TestQueryOverRestrictionWithClause() diff --git a/src/NHibernate.Test/NHSpecificTest/GH1994/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/GH1994/Mappings.hbm.xml index 57a58acefa8..449a4b8bc97 100644 --- a/src/NHibernate.Test/NHSpecificTest/GH1994/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/GH1994/Mappings.hbm.xml @@ -18,6 +18,12 @@ + + + + + + diff --git a/src/NHibernate.Test/NHSpecificTest/NH750/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/NH750/Fixture.cs index b3b38396591..9ff71f07057 100644 --- a/src/NHibernate.Test/NHSpecificTest/NH750/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/NH750/Fixture.cs @@ -1,4 +1,5 @@ using System; +using NHibernate.Cfg; using NUnit.Framework; namespace NHibernate.Test.NHSpecificTest.NH750 @@ -16,6 +17,17 @@ protected override void OnTearDown() } } + protected override string CacheConcurrencyStrategy + { + get { return null; } + } + + protected override void Configure(Configuration configuration) + { + configuration.SetProperty(Cfg.Environment.UseSecondLevelCache, "false"); + base.Configure(configuration); + } + [Test] public void DeviceOfDrive() { @@ -55,7 +67,8 @@ public void DeviceOfDrive() dv2 = (Device) s.Load(typeof(Device), dvSavedId[1]); } Assert.AreEqual(2, dv1.Drives.Count); - Assert.AreEqual(2, dv2.Drives.Count); + // Verify one is missing + Assert.AreEqual(1, dv2.Drives.Count); // Verify dv1 unchanged Assert.IsTrue(dv1.Drives.Contains(dr1)); Assert.IsTrue(dv1.Drives.Contains(dr2)); @@ -63,13 +76,43 @@ public void DeviceOfDrive() // Verify dv2 Assert.IsTrue(dv2.Drives.Contains(dr1)); Assert.IsFalse(dv2.Drives.Contains(dr3)); - // Verify one null - int nullCount = 0; - for (int i = 0; i < dv2.Drives.Count; i++) + + //Make sure that flush didn't touch not-found="ignore" records for not modified collection + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) { - if (dv2.Drives[i] == null) nullCount++; + dv2 = s.Get(dv2.Id); + s.Flush(); + t.Commit(); + } + + VerifyResult(expectedInCollection: 1, expectedInDb: 2, msg: "not modified collection"); + + //Many-to-many clears collection and recreates it so not-found ignore records are lost + using (var s = Sfi.OpenSession()) + using (var t = s.BeginTransaction()) + { + dv2 = s.Get(dv2.Id); + dv2.Drives.Add(dr2); + t.Commit(); + } + + VerifyResult(2, 2, msg: "modified collection"); + + void VerifyResult(int expectedInCollection, int expectedInDb, string msg) + { + using (var s = Sfi.OpenSession()) + { + var realCound = Convert.ToInt32( + s.CreateSQLQuery("select count(*) from DriveOfDevice where DeviceId = :id ") + .SetParameter("id", dv2.Id) + .UniqueResult()); + dv2 = s.Get(dv2.Id); + + Assert.That(dv2.Drives.Count, Is.EqualTo(expectedInCollection), msg); + Assert.That(realCound, Is.EqualTo(expectedInDb), msg); + } } - Assert.AreEqual(1, nullCount); } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Async/Collection/Generic/PersistentGenericBag.cs b/src/NHibernate/Async/Collection/Generic/PersistentGenericBag.cs index fc1d89befe1..820d03c497d 100644 --- a/src/NHibernate/Async/Collection/Generic/PersistentGenericBag.cs +++ b/src/NHibernate/Async/Collection/Generic/PersistentGenericBag.cs @@ -154,11 +154,9 @@ public override async Task ReadFromAsync(DbDataReader reader, ICollectio // note that if we load this collection from a cartesian product // the multiplicity would be broken ... so use an idbag instead var element = await (role.ReadElementAsync(reader, owner, descriptor.SuffixedElementAliases, Session, cancellationToken)).ConfigureAwait(false); - // NH Different behavior : we don't check for null - // The NH-750 test show how checking for null we are ignoring the not-found tag and - // the DB may have some records ignored by NH. This issue may need some more deep consideration. - //if (element != null) - _gbag.Add((T) element); + + if (element != null) + _gbag.Add((T) element); return element; } } diff --git a/src/NHibernate/Collection/Generic/PersistentGenericBag.cs b/src/NHibernate/Collection/Generic/PersistentGenericBag.cs index 67564a5de3e..d58be9c9a94 100644 --- a/src/NHibernate/Collection/Generic/PersistentGenericBag.cs +++ b/src/NHibernate/Collection/Generic/PersistentGenericBag.cs @@ -448,11 +448,9 @@ public override object ReadFrom(DbDataReader reader, ICollectionPersister role, // note that if we load this collection from a cartesian product // the multiplicity would be broken ... so use an idbag instead var element = role.ReadElement(reader, owner, descriptor.SuffixedElementAliases, Session); - // NH Different behavior : we don't check for null - // The NH-750 test show how checking for null we are ignoring the not-found tag and - // the DB may have some records ignored by NH. This issue may need some more deep consideration. - //if (element != null) - _gbag.Add((T) element); + + if (element != null) + _gbag.Add((T) element); return element; }