diff --git a/doc/reference/modules/basic_mapping.xml b/doc/reference/modules/basic_mapping.xml
index bf6b2f4342d..f30fa154507 100644
--- a/doc/reference/modules/basic_mapping.xml
+++ b/doc/reference/modules/basic_mapping.xml
@@ -1694,6 +1694,7 @@
+
lazy (optional - defaults to false ):
specifies that this property is lazy. A lazy property is not loaded when
the object is initially loaded, unless the fetch mode has been overridden
- in a specific query. Values for lazy properties are loaded when any lazy
- property of the object is accessed. Having lazy properties causes instances
- of the entity to be loaded as proxies. Theses proxies ignore the class
- proxy setting and always derives from the persistent class,
- requiring its members to be overridable.
+ in a specific query. Values for lazy properties are loaded per lazy-group.
+
+
+ Having lazy properties causes instances of the entity to be loaded as proxies.
+ Theses proxies ignore the class proxy setting and always
+ derives from the persistent class, requiring its members to be overridable.
+
+
+
+
+ lazy-group (optional - defaults to DEFAULT ):
+ if the property is lazy, its lazy-loading group. When a lazy property is accessed,
+ the other lazy properties of the lazy group are also loaded with it.
-
+
not-null (optional - defaults to false ):
sets the column nullability for DDL generation.
-
+
unique (optional - defaults to false ):
sets the column uniqueness for DDL generation. Use unique-key
instead if the value is unique only in combination with other properties.
-
+
unique-key (optional):
a logical name for an unique index for DDL generation. The column will be included in
@@ -1801,7 +1811,7 @@
logical name. The actual index name depends on the dialect.
-
+
index (optional):
a logical name for an index for DDL generation. The column will be included in
@@ -1809,19 +1819,19 @@
name. The actual index name depends on the dialect.
-
+
length (optional): if the type takes a length and does not
already specify it, its length.
-
+
precision (optional): if the type takes a precision and does not
already specify it, its precision.
-
+
scale (optional): if the type takes a scale and does not
already specify it, its scale.
@@ -2598,6 +2608,7 @@
+
@@ -2660,6 +2672,14 @@
+
+ lazy-group (optional - defaults to DEFAULT ):
+ If the component is lazy, its lazy-loading group. When a lazy property is accessed
+ on an object, included when the property is a component, the other lazy properties
+ of the lazy group are also loaded with it.
+
+
+
unique (optional - defaults to false ): Specifies
that an unique constraint exists upon all mapped columns of the component.
diff --git a/src/NHibernate.DomainModel/Async/CustomPersister.cs b/src/NHibernate.DomainModel/Async/CustomPersister.cs
index 104cc514c36..9a671f2443b 100644
--- a/src/NHibernate.DomainModel/Async/CustomPersister.cs
+++ b/src/NHibernate.DomainModel/Async/CustomPersister.cs
@@ -85,9 +85,8 @@ public async Task LoadAsync(object id, object optionalObject, LockMode l
if (obj != null)
{
clone = (Custom)obj.Clone();
- TwoPhaseLoad.AddUninitializedEntity(session.GenerateEntityKey(id, this), clone, this, LockMode.None, false,
- session);
- TwoPhaseLoad.PostHydrate(this, id, new String[] {obj.Name}, null, clone, LockMode.None, false, session);
+ TwoPhaseLoad.AddUninitializedEntity(session.GenerateEntityKey(id, this), clone, this, LockMode.None, session);
+ TwoPhaseLoad.PostHydrate(this, id, new String[] {obj.Name}, null, clone, LockMode.None, session);
await (TwoPhaseLoad.InitializeEntityAsync(clone, false, session, new PreLoadEvent((IEventSource) session),
new PostLoadEvent((IEventSource) session), cancellationToken));
}
diff --git a/src/NHibernate.DomainModel/CustomPersister.cs b/src/NHibernate.DomainModel/CustomPersister.cs
index 04ce8d6625b..a4f91c22644 100644
--- a/src/NHibernate.DomainModel/CustomPersister.cs
+++ b/src/NHibernate.DomainModel/CustomPersister.cs
@@ -310,9 +310,8 @@ public object Load(object id, object optionalObject, LockMode lockMode, ISession
if (obj != null)
{
clone = (Custom)obj.Clone();
- TwoPhaseLoad.AddUninitializedEntity(session.GenerateEntityKey(id, this), clone, this, LockMode.None, false,
- session);
- TwoPhaseLoad.PostHydrate(this, id, new String[] {obj.Name}, null, clone, LockMode.None, false, session);
+ TwoPhaseLoad.AddUninitializedEntity(session.GenerateEntityKey(id, this), clone, this, LockMode.None, session);
+ TwoPhaseLoad.PostHydrate(this, id, new String[] {obj.Name}, null, clone, LockMode.None, session);
TwoPhaseLoad.InitializeEntity(clone, false, session, new PreLoadEvent((IEventSource) session),
new PostLoadEvent((IEventSource) session));
}
diff --git a/src/NHibernate.Test/Async/CacheTest/SerializationFixture.cs b/src/NHibernate.Test/Async/CacheTest/SerializationFixture.cs
index 4c16e4bf28a..85d822288c9 100644
--- a/src/NHibernate.Test/Async/CacheTest/SerializationFixture.cs
+++ b/src/NHibernate.Test/Async/CacheTest/SerializationFixture.cs
@@ -142,8 +142,7 @@ private CacheEntry CreateCacheEntry()
{
DisassembledState = GetAllKnownTypeValues(),
Version = 55,
- Subclass = "Test",
- AreLazyPropertiesUnfetched = true
+ Subclass = "Test"
};
}
@@ -240,7 +239,6 @@ private void CheckCacheEntry(CacheEntry original, CacheEntry copy)
Assert.That(copy.Version, Is.EqualTo(original.Version));
Assert.That(copy.Version, Is.TypeOf(original.Version.GetType()));
Assert.That(copy.Subclass, Is.EqualTo(original.Subclass));
- Assert.That(copy.AreLazyPropertiesUnfetched, Is.EqualTo(original.AreLazyPropertiesUnfetched));
for (var i = 0; i < copy.DisassembledState.Length; i++)
{
Assert.That(copy.DisassembledState[i], Is.TypeOf(original.DisassembledState[i].GetType()));
diff --git a/src/NHibernate.Test/Async/LazyGroup/LazyGroupFixture.cs b/src/NHibernate.Test/Async/LazyGroup/LazyGroupFixture.cs
new file mode 100644
index 00000000000..149e7b001f7
--- /dev/null
+++ b/src/NHibernate.Test/Async/LazyGroup/LazyGroupFixture.cs
@@ -0,0 +1,230 @@
+//------------------------------------------------------------------------------
+//
+// 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.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NHibernate.Cache;
+using NHibernate.Cfg;
+using NUnit.Framework;
+
+namespace NHibernate.Test.LazyGroup
+{
+ using System.Threading;
+ [TestFixture]
+ public class LazyGroupFixtureAsync : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override string[] Mappings => new[] { "LazyGroup.Mappings.hbm.xml" };
+
+ protected override void Configure(Configuration configuration)
+ {
+ base.Configure(configuration);
+ configuration.Properties[Environment.CacheProvider] = typeof(HashtableCacheProvider).AssemblyQualifiedName;
+ configuration.Properties[Environment.UseSecondLevelCache] = "true";
+ configuration.Properties[Environment.GenerateStatistics] = "true";
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ for (var i = 1; i <= 5; i++)
+ {
+ var person = GeneratePerson(i);
+ s.Save(person);
+ }
+
+ tx.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ s.CreateQuery("delete from Person").ExecuteUpdate();
+ tx.Commit();
+ }
+ }
+
+ [Test]
+ public async Task TestGroupsAsync()
+ {
+ using (var s = OpenSession())
+ {
+ var person = await (s.GetAsync(1));
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Name"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+
+ var nickName = person.NickName;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(nickName, Is.EqualTo("NickName1"));
+
+ var address = person.Address;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(address.City, Is.EqualTo("City1"));
+ Assert.That(address.Street, Is.EqualTo("Street1"));
+ Assert.That(address.PostCode, Is.EqualTo(1001));
+
+ var image = person.Image;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(person.Image, Has.Length.EqualTo(1));
+
+ var age = person.Age;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.True);
+ Assert.That(person.Age, Is.EqualTo(1));
+ }
+ }
+
+ [TestCase(true)]
+ [TestCase(false)]
+ public async Task TestUpdateAsync(bool fetchBeforeUpdate, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = await (s.GetAsync(1, cancellationToken));
+ if (fetchBeforeUpdate)
+ {
+ var nickName = person.NickName;
+ }
+
+ person.NickName = "test";
+
+ await (tx.CommitAsync(cancellationToken));
+ }
+
+ Assert.That(Sfi.Statistics.EntityUpdateCount, Is.EqualTo(1));
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = await (s.GetAsync(1, cancellationToken));
+ Assert.That(person.NickName, Is.EqualTo("test"));
+
+ person.NickName = "NickName1"; // reset name
+
+ await (tx.CommitAsync(cancellationToken));
+ }
+ }
+
+ [Test]
+ public async Task TestCacheAsync()
+ {
+ var persister = Sfi.GetEntityPersister(typeof(Person).FullName);
+ var cache = (HashtableCache) persister.Cache.Cache;
+ await (cache.ClearAsync(CancellationToken.None));
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = await (s.GetAsync(1));
+
+ var nickName = person.NickName;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(nickName, Is.EqualTo("NickName1"));
+
+ await (tx.CommitAsync());
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = await (s.GetAsync(1));
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(person.NickName, Is.EqualTo("NickName1"));
+
+ await (tx.CommitAsync());
+ }
+ }
+
+ [Test]
+ public async Task TestInitializeFromCacheAsync()
+ {
+ var persister = Sfi.GetEntityPersister(typeof(Person).FullName);
+ var cache = (HashtableCache) persister.Cache.Cache;
+ await (cache.ClearAsync(CancellationToken.None));
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = await (s.GetAsync(1));
+
+ await (InitializeImageAsync());
+
+ Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(2));
+
+ var image = person.Image; // Should be initialized from cache
+
+ Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(2));
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(image, Has.Length.EqualTo(1));
+
+ await (tx.CommitAsync());
+ }
+ }
+
+ private async Task InitializeImageAsync(CancellationToken cancellationToken = default(CancellationToken))
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = await (s.GetAsync(1, cancellationToken));
+ var image = person.Image;
+
+ await (tx.CommitAsync(cancellationToken));
+ }
+ }
+
+ private static Person GeneratePerson(int i)
+ {
+ return new Person
+ {
+ Id = i,
+ Name = $"Person{i}",
+ Address = new Address
+ {
+ City = $"City{i}",
+ PostCode = 1000+i,
+ Street = $"Street{i}"
+ },
+ Image = new byte[i],
+ NickName = $"NickName{i}"
+ };
+
+ }
+ }
+}
diff --git a/src/NHibernate.Test/CacheTest/SerializationFixture.cs b/src/NHibernate.Test/CacheTest/SerializationFixture.cs
index 7d8166a297c..eb21721f45b 100644
--- a/src/NHibernate.Test/CacheTest/SerializationFixture.cs
+++ b/src/NHibernate.Test/CacheTest/SerializationFixture.cs
@@ -131,8 +131,7 @@ private CacheEntry CreateCacheEntry()
{
DisassembledState = GetAllKnownTypeValues(),
Version = 55,
- Subclass = "Test",
- AreLazyPropertiesUnfetched = true
+ Subclass = "Test"
};
}
@@ -229,7 +228,6 @@ private void CheckCacheEntry(CacheEntry original, CacheEntry copy)
Assert.That(copy.Version, Is.EqualTo(original.Version));
Assert.That(copy.Version, Is.TypeOf(original.Version.GetType()));
Assert.That(copy.Subclass, Is.EqualTo(original.Subclass));
- Assert.That(copy.AreLazyPropertiesUnfetched, Is.EqualTo(original.AreLazyPropertiesUnfetched));
for (var i = 0; i < copy.DisassembledState.Length; i++)
{
Assert.That(copy.DisassembledState[i], Is.TypeOf(original.DisassembledState[i].GetType()));
diff --git a/src/NHibernate.Test/LazyGroup/Address.cs b/src/NHibernate.Test/LazyGroup/Address.cs
new file mode 100644
index 00000000000..5699d3db61f
--- /dev/null
+++ b/src/NHibernate.Test/LazyGroup/Address.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NHibernate.Test.LazyGroup
+{
+ public class Address
+ {
+ public string City { get; set; }
+
+ public string Street { get; set; }
+
+ public int PostCode { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/LazyGroup/LazyGroupFixture.cs b/src/NHibernate.Test/LazyGroup/LazyGroupFixture.cs
new file mode 100644
index 00000000000..82b51aa0c4a
--- /dev/null
+++ b/src/NHibernate.Test/LazyGroup/LazyGroupFixture.cs
@@ -0,0 +1,219 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NHibernate.Cache;
+using NHibernate.Cfg;
+using NUnit.Framework;
+
+namespace NHibernate.Test.LazyGroup
+{
+ [TestFixture]
+ public class LazyGroupFixture : TestCase
+ {
+ protected override string MappingsAssembly => "NHibernate.Test";
+
+ protected override string[] Mappings => new[] { "LazyGroup.Mappings.hbm.xml" };
+
+ protected override void Configure(Configuration configuration)
+ {
+ base.Configure(configuration);
+ configuration.Properties[Environment.CacheProvider] = typeof(HashtableCacheProvider).AssemblyQualifiedName;
+ configuration.Properties[Environment.UseSecondLevelCache] = "true";
+ configuration.Properties[Environment.GenerateStatistics] = "true";
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ for (var i = 1; i <= 5; i++)
+ {
+ var person = GeneratePerson(i);
+ s.Save(person);
+ }
+
+ tx.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ s.CreateQuery("delete from Person").ExecuteUpdate();
+ tx.Commit();
+ }
+ }
+
+ [Test]
+ public void TestGroups()
+ {
+ using (var s = OpenSession())
+ {
+ var person = s.Get(1);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Name"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+
+ var nickName = person.NickName;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(nickName, Is.EqualTo("NickName1"));
+
+ var address = person.Address;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(address.City, Is.EqualTo("City1"));
+ Assert.That(address.Street, Is.EqualTo("Street1"));
+ Assert.That(address.PostCode, Is.EqualTo(1001));
+
+ var image = person.Image;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(person.Image, Has.Length.EqualTo(1));
+
+ var age = person.Age;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.True);
+ Assert.That(person.Age, Is.EqualTo(1));
+ }
+ }
+
+ [TestCase(true)]
+ [TestCase(false)]
+ public void TestUpdate(bool fetchBeforeUpdate)
+ {
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = s.Get(1);
+ if (fetchBeforeUpdate)
+ {
+ var nickName = person.NickName;
+ }
+
+ person.NickName = "test";
+
+ tx.Commit();
+ }
+
+ Assert.That(Sfi.Statistics.EntityUpdateCount, Is.EqualTo(1));
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = s.Get(1);
+ Assert.That(person.NickName, Is.EqualTo("test"));
+
+ person.NickName = "NickName1"; // reset name
+
+ tx.Commit();
+ }
+ }
+
+ [Test]
+ public void TestCache()
+ {
+ var persister = Sfi.GetEntityPersister(typeof(Person).FullName);
+ var cache = (HashtableCache) persister.Cache.Cache;
+ cache.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = s.Get(1);
+
+ var nickName = person.NickName;
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(nickName, Is.EqualTo("NickName1"));
+
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = s.Get(1);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(person.NickName, Is.EqualTo("NickName1"));
+
+ tx.Commit();
+ }
+ }
+
+ [Test]
+ public void TestInitializeFromCache()
+ {
+ var persister = Sfi.GetEntityPersister(typeof(Person).FullName);
+ var cache = (HashtableCache) persister.Cache.Cache;
+ cache.Clear();
+ Sfi.Statistics.Clear();
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = s.Get(1);
+
+ InitializeImage();
+
+ Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(2));
+
+ var image = person.Image; // Should be initialized from cache
+
+ Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(2));
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "NickName"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Address"), Is.False);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Image"), Is.True);
+ Assert.That(NHibernateUtil.IsPropertyInitialized(person, "Age"), Is.False);
+ Assert.That(image, Has.Length.EqualTo(1));
+
+ tx.Commit();
+ }
+ }
+
+ private void InitializeImage()
+ {
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var person = s.Get(1);
+ var image = person.Image;
+
+ tx.Commit();
+ }
+ }
+
+ private static Person GeneratePerson(int i)
+ {
+ return new Person
+ {
+ Id = i,
+ Name = $"Person{i}",
+ Address = new Address
+ {
+ City = $"City{i}",
+ PostCode = 1000+i,
+ Street = $"Street{i}"
+ },
+ Image = new byte[i],
+ NickName = $"NickName{i}"
+ };
+
+ }
+ }
+}
diff --git a/src/NHibernate.Test/LazyGroup/Mappings.hbm.xml b/src/NHibernate.Test/LazyGroup/Mappings.hbm.xml
new file mode 100644
index 00000000000..68376553227
--- /dev/null
+++ b/src/NHibernate.Test/LazyGroup/Mappings.hbm.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NHibernate.Test/LazyGroup/Person.cs b/src/NHibernate.Test/LazyGroup/Person.cs
new file mode 100644
index 00000000000..0c7d57ae49b
--- /dev/null
+++ b/src/NHibernate.Test/LazyGroup/Person.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NHibernate.Test.LazyGroup
+{
+ public class Person
+ {
+ public virtual int Id { get; set; }
+
+ public virtual string Name { get; set; }
+
+ public virtual string NickName { get; set; }
+
+ public virtual Address Address { get; set; }
+
+ public virtual byte[] Image { get; set; }
+
+ public virtual long Age { get; protected set; }
+ }
+}
diff --git a/src/NHibernate/Action/EntityInsertAction.cs b/src/NHibernate/Action/EntityInsertAction.cs
index 6f4f9a71a45..71250f48373 100644
--- a/src/NHibernate/Action/EntityInsertAction.cs
+++ b/src/NHibernate/Action/EntityInsertAction.cs
@@ -74,7 +74,7 @@ public override void Execute()
if (IsCachePutEnabled(persister))
{
- CacheEntry ce = CacheEntry.Create(State, persister, persister.HasUninitializedLazyProperties(instance), version, session, instance);
+ CacheEntry ce = CacheEntry.Create(State, persister, version, session, instance);
cacheEntry = persister.CacheEntryStructure.Structure(ce);
CacheKey ck = Session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName);
diff --git a/src/NHibernate/Action/EntityUpdateAction.cs b/src/NHibernate/Action/EntityUpdateAction.cs
index dfac0d86120..8b3ded4b24d 100644
--- a/src/NHibernate/Action/EntityUpdateAction.cs
+++ b/src/NHibernate/Action/EntityUpdateAction.cs
@@ -114,7 +114,7 @@ public override void Execute()
}
else
{
- CacheEntry ce = CacheEntry.Create(state, persister, persister.HasUninitializedLazyProperties(instance), nextVersion, Session, instance);
+ CacheEntry ce = CacheEntry.Create(state, persister, nextVersion, Session, instance);
cacheEntry = persister.CacheEntryStructure.Structure(ce);
bool put = persister.Cache.Update(ck, cacheEntry, nextVersion, previousVersion);
diff --git a/src/NHibernate/Async/Action/EntityInsertAction.cs b/src/NHibernate/Async/Action/EntityInsertAction.cs
index 4a0115dc06a..dced95776c3 100644
--- a/src/NHibernate/Async/Action/EntityInsertAction.cs
+++ b/src/NHibernate/Async/Action/EntityInsertAction.cs
@@ -70,7 +70,7 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken)
if (IsCachePutEnabled(persister))
{
- CacheEntry ce = await (CacheEntry.CreateAsync(State, persister, persister.HasUninitializedLazyProperties(instance), version, session, instance, cancellationToken)).ConfigureAwait(false);
+ CacheEntry ce = await (CacheEntry.CreateAsync(State, persister, version, session, instance, cancellationToken)).ConfigureAwait(false);
cacheEntry = persister.CacheEntryStructure.Structure(ce);
CacheKey ck = Session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName);
diff --git a/src/NHibernate/Async/Action/EntityUpdateAction.cs b/src/NHibernate/Async/Action/EntityUpdateAction.cs
index 33f1cab8fdc..89b065f40aa 100644
--- a/src/NHibernate/Async/Action/EntityUpdateAction.cs
+++ b/src/NHibernate/Async/Action/EntityUpdateAction.cs
@@ -99,7 +99,7 @@ public override async Task ExecuteAsync(CancellationToken cancellationToken)
}
else
{
- CacheEntry ce = await (CacheEntry.CreateAsync(state, persister, persister.HasUninitializedLazyProperties(instance), nextVersion, Session, instance, cancellationToken)).ConfigureAwait(false);
+ CacheEntry ce = await (CacheEntry.CreateAsync(state, persister, nextVersion, Session, instance, cancellationToken)).ConfigureAwait(false);
cacheEntry = persister.CacheEntryStructure.Structure(ce);
bool put = await (persister.Cache.UpdateAsync(ck, cacheEntry, nextVersion, previousVersion, cancellationToken)).ConfigureAwait(false);
diff --git a/src/NHibernate/Async/Cache/Entry/CacheEntry.cs b/src/NHibernate/Async/Cache/Entry/CacheEntry.cs
index 4fd9c71f288..e6aca27123e 100644
--- a/src/NHibernate/Async/Cache/Entry/CacheEntry.cs
+++ b/src/NHibernate/Async/Cache/Entry/CacheEntry.cs
@@ -22,6 +22,8 @@ namespace NHibernate.Cache.Entry
public sealed partial class CacheEntry
{
+ // Since 5.3
+ [Obsolete("Use the overload without unfetched parameter instead.")]
public static async Task CreateAsync(object[] state, IEntityPersister persister, bool unfetched, object version,
ISessionImplementor session, object owner, CancellationToken cancellationToken)
{
@@ -41,6 +43,24 @@ public static async Task CreateAsync(object[] state, IEntityPersiste
};
}
+ public static async Task CreateAsync(object[] state, IEntityPersister persister, object version,
+ ISessionImplementor session, object owner, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return new CacheEntry
+ {
+ //disassembled state gets put in a new array (we write to cache by value!)
+ DisassembledState = await (TypeHelper.DisassembleAsync(
+ state,
+ persister.PropertyTypes,
+ persister.IsLazyPropertiesCacheable ? null : persister.PropertyLaziness,
+ session,
+ owner, cancellationToken)).ConfigureAwait(false),
+ Subclass = persister.EntityName,
+ Version = version
+ };
+ }
+
public Task AssembleAsync(object instance, object id, IEntityPersister persister, IInterceptor interceptor,
ISessionImplementor session, CancellationToken cancellationToken)
{
diff --git a/src/NHibernate/Async/Engine/IPersistenceContext.cs b/src/NHibernate/Async/Engine/IPersistenceContext.cs
index 82b2a48d3d4..3807b2649a9 100644
--- a/src/NHibernate/Async/Engine/IPersistenceContext.cs
+++ b/src/NHibernate/Async/Engine/IPersistenceContext.cs
@@ -8,10 +8,13 @@
//------------------------------------------------------------------------------
+using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using NHibernate.Collection;
using NHibernate.Engine.Loading;
+using NHibernate.Intercept;
using NHibernate.Persister.Collection;
using NHibernate.Persister.Entity;
using NHibernate.Proxy;
diff --git a/src/NHibernate/Async/Engine/TwoPhaseLoad.cs b/src/NHibernate/Async/Engine/TwoPhaseLoad.cs
index bd8948df083..6b9b84048d2 100644
--- a/src/NHibernate/Async/Engine/TwoPhaseLoad.cs
+++ b/src/NHibernate/Async/Engine/TwoPhaseLoad.cs
@@ -28,7 +28,7 @@ namespace NHibernate.Engine
using System.Threading;
public static partial class TwoPhaseLoad
{
-
+
///
/// Perform the second step of 2-phase load. Fully initialize the entity instance.
/// After processing a JDBC result set, we "resolve" all the associations
@@ -124,7 +124,7 @@ internal static async Task InitializeEntityAsync(object entity, bool readOnly, I
object version = Versioning.GetVersion(hydratedState, persister);
CacheEntry entry =
- await (CacheEntry.CreateAsync(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity, cancellationToken)).ConfigureAwait(false);
+ await (CacheEntry.CreateAsync(hydratedState, persister, version, session, entity, cancellationToken)).ConfigureAwait(false);
CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName);
if (cacheBatchingHandler != null && persister.IsBatchLoadable)
@@ -184,7 +184,7 @@ internal static async Task InitializeEntityAsync(object entity, bool readOnly, I
persistenceContext.SetEntryStatus(entityEntry, Status.Loaded);
}
- persister.AfterInitialize(entity, entityEntry.LoadedWithLazyPropertiesUnfetched, session);
+ persister.AfterInitialize(entity, session);
if (session.IsEventSource)
{
diff --git a/src/NHibernate/Async/Event/Default/AbstractReassociateEventListener.cs b/src/NHibernate/Async/Event/Default/AbstractReassociateEventListener.cs
index 30b74e9089b..9add7a34910 100644
--- a/src/NHibernate/Async/Event/Default/AbstractReassociateEventListener.cs
+++ b/src/NHibernate/Async/Event/Default/AbstractReassociateEventListener.cs
@@ -59,8 +59,7 @@ protected async Task ReassociateAsync(AbstractEvent @event, object
LockMode.None,
true,
persister,
- false,
- true);
+ false);
await (new OnLockVisitor(source, id, entity).ProcessAsync(entity, persister, cancellationToken)).ConfigureAwait(false);
diff --git a/src/NHibernate/Async/Event/Default/AbstractSaveEventListener.cs b/src/NHibernate/Async/Event/Default/AbstractSaveEventListener.cs
index d58c8ae8377..985ead87deb 100644
--- a/src/NHibernate/Async/Event/Default/AbstractSaveEventListener.cs
+++ b/src/NHibernate/Async/Event/Default/AbstractSaveEventListener.cs
@@ -191,7 +191,7 @@ protected virtual async Task PerformSaveOrReplicateAsync(object entity,
// Put a placeholder in entries, so we don't recurse back and try to save() the
// same object again. QUESTION: should this be done before onSave() is called?
// likewise, should it be done before onUpdate()?
- source.PersistenceContext.AddEntry(entity, Status.Saving, null, null, id, null, LockMode.Write, useIdentityColumn, persister, false, false);
+ source.PersistenceContext.AddEntry(entity, Status.Saving, null, null, id, null, LockMode.Write, useIdentityColumn, persister, false);
await (CascadeBeforeSaveAsync(source, persister, entity, anything, cancellationToken)).ConfigureAwait(false);
@@ -253,8 +253,7 @@ protected virtual async Task PerformSaveOrReplicateAsync(object entity,
LockMode.Write,
useIdentityColumn,
persister,
- VersionIncrementDisabled,
- false);
+ VersionIncrementDisabled);
//source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );
if (!useIdentityColumn)
diff --git a/src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs
index fec2ff09a11..be81efc248f 100644
--- a/src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs
+++ b/src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs
@@ -93,7 +93,6 @@ public virtual async Task OnDeleteAsync(DeleteEvent @event, ISet transie
LockMode.None,
true,
persister,
- false,
false);
}
else
diff --git a/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs
index 38e4a7a1c7f..20a32b8518a 100644
--- a/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs
+++ b/src/NHibernate/Async/Event/Default/DefaultLoadEventListener.cs
@@ -505,7 +505,7 @@ private async Task AssembleCacheEntryAsync(CacheEntry entry, object id,
// make it circular-reference safe
EntityKey entityKey = session.GenerateEntityKey(id, subclassPersister);
- TwoPhaseLoad.AddUninitializedCachedEntity(entityKey, result, subclassPersister, LockMode.None, entry.AreLazyPropertiesUnfetched, entry.Version, session);
+ TwoPhaseLoad.AddUninitializedCachedEntity(entityKey, result, subclassPersister, LockMode.None, entry.Version, session);
IType[] types = subclassPersister.PropertyTypes;
object[] values = await (entry.AssembleAsync(result, id, subclassPersister, session.Interceptor, session, cancellationToken)).ConfigureAwait(false); // intializes result by side-effect
@@ -543,10 +543,9 @@ private async Task AssembleCacheEntryAsync(CacheEntry entry, object id,
LockMode.None,
true,
subclassPersister,
- false,
- entry.AreLazyPropertiesUnfetched);
+ false);
- subclassPersister.AfterInitialize(result, entry.AreLazyPropertiesUnfetched, session);
+ subclassPersister.AfterInitialize(result, session);
await (persistenceContext.InitializeNonLazyCollectionsAsync(cancellationToken)).ConfigureAwait(false);
// upgrade the lock if necessary:
//lock(result, lockMode);
diff --git a/src/NHibernate/Async/Event/Default/DefaultReplicateEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultReplicateEventListener.cs
index 04f646f475c..124f45d1755 100644
--- a/src/NHibernate/Async/Event/Default/DefaultReplicateEventListener.cs
+++ b/src/NHibernate/Async/Event/Default/DefaultReplicateEventListener.cs
@@ -127,8 +127,7 @@ private async Task PerformReplicationAsync(object entity, object id, object vers
LockMode.None,
true,
persister,
- true,
- false);
+ true);
await (CascadeAfterReplicateAsync(entity, persister, replicationMode, source, cancellationToken)).ConfigureAwait(false);
}
diff --git a/src/NHibernate/Async/Event/Default/DefaultSaveOrUpdateEventListener.cs b/src/NHibernate/Async/Event/Default/DefaultSaveOrUpdateEventListener.cs
index 5bb29def684..99aa1b95e7a 100644
--- a/src/NHibernate/Async/Event/Default/DefaultSaveOrUpdateEventListener.cs
+++ b/src/NHibernate/Async/Event/Default/DefaultSaveOrUpdateEventListener.cs
@@ -223,8 +223,7 @@ protected virtual async Task PerformUpdateAsync(SaveOrUpdateEvent @event, object
LockMode.None,
true,
persister,
- false,
- true);
+ false);
//persister.AfterReassociate(entity, source); TODO H3.2 not ported
diff --git a/src/NHibernate/Async/Loader/Loader.cs b/src/NHibernate/Async/Loader/Loader.cs
index 9d37f545940..bf7d06b1046 100644
--- a/src/NHibernate/Async/Loader/Loader.cs
+++ b/src/NHibernate/Async/Loader/Loader.cs
@@ -785,15 +785,10 @@ private async Task UpdateLazyPropertiesFromResultSetAsync(DbDataReader rs, int i
Action cacheBatchingHandler, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
- if (!entry.LoadedWithLazyPropertiesUnfetched)
- {
- return; // All lazy properties were already loaded
- }
-
- var eagerPropertyFetch = IsEagerPropertyFetchEnabled(i);
+ var fetchAllProperties = IsEagerPropertyFetchEnabled(i);
var fetchLazyProperties = GetFetchLazyProperties(i);
- if (!eagerPropertyFetch && fetchLazyProperties == null)
+ if (!fetchAllProperties && fetchLazyProperties == null)
{
return; // No lazy properties were loaded
}
@@ -826,11 +821,19 @@ private async Task UpdateLazyPropertiesFromResultSetAsync(DbDataReader rs, int i
? EntityAliases[i].SuffixedPropertyAliases
: GetSubclassEntityAliases(i, persister);
- if (!await (persister.InitializeLazyPropertiesAsync(rs, id, obj, rootPersister, cols, updateLazyProperties, eagerPropertyFetch, session, cancellationToken)).ConfigureAwait(false))
+ if (!await (persister.InitializeLazyPropertiesAsync(rs, id, obj, rootPersister, cols, updateLazyProperties, fetchAllProperties, session, cancellationToken)).ConfigureAwait(false))
{
return;
}
+ await (UpdateCacheForEntityAsync(obj, id, entry, persister, session, cacheBatchingHandler, cancellationToken)).ConfigureAwait(false);
+ }
+
+ internal static async Task UpdateCacheForEntityAsync(
+ object obj, object id, EntityEntry entry, IEntityPersister persister, ISessionImplementor session,
+ Action cacheBatchingHandler, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
if (entry.Status == Status.Loading || !persister.HasCache ||
!session.CacheMode.HasFlag(CacheMode.Put) || !persister.IsLazyPropertiesCacheable)
{
@@ -845,7 +848,7 @@ private async Task UpdateLazyPropertiesFromResultSetAsync(DbDataReader rs, int i
var factory = session.Factory;
var state = persister.GetPropertyValues(obj);
var version = Versioning.GetVersion(state, persister);
- var cacheEntry = await (CacheEntry.CreateAsync(state, persister, entry.LoadedWithLazyPropertiesUnfetched, version, session, obj, cancellationToken)).ConfigureAwait(false);
+ var cacheEntry = await (CacheEntry.CreateAsync(state, persister, version, session, obj, cancellationToken)).ConfigureAwait(false);
var cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName);
if (cacheBatchingHandler != null && persister.IsBatchLoadable)
@@ -895,23 +898,23 @@ private async Task LoadFromResultSetAsync(DbDataReader rs, int i, object obj, st
Log.Debug("Initializing object from DataReader: {0}", MessageHelper.InfoString(persister, id));
}
- bool eagerPropertyFetch = IsEagerPropertyFetchEnabled(i);
+ bool fetchAllProperties = IsEagerPropertyFetchEnabled(i);
var eagerFetchProperties = GetFetchLazyProperties(i);
// add temp entry so that the next step is circular-reference
// safe - only needed because some types don't take proper
// advantage of two-phase-load (esp. components)
- TwoPhaseLoad.AddUninitializedEntity(key, obj, persister, lockMode, !eagerPropertyFetch, session);
+ TwoPhaseLoad.AddUninitializedEntity(key, obj, persister, lockMode, session);
string[][] cols = persister == rootPersister
? EntityAliases[i].SuffixedPropertyAliases
: GetSubclassEntityAliases(i, persister);
- object[] values = await (persister.HydrateAsync(rs, id, obj, cols, eagerFetchProperties, eagerPropertyFetch, session, cancellationToken)).ConfigureAwait(false);
+ object[] values = await (persister.HydrateAsync(rs, id, obj, cols, eagerFetchProperties, fetchAllProperties, session, cancellationToken)).ConfigureAwait(false);
object rowId = persister.HasRowId ? rs[EntityAliases[i].RowIdAlias] : null;
- TwoPhaseLoad.PostHydrate(persister, id, values, rowId, obj, lockMode, !eagerPropertyFetch, session);
+ TwoPhaseLoad.PostHydrate(persister, id, values, rowId, obj, lockMode, session);
}
///
diff --git a/src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs b/src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs
index 9746bba3833..43d5f59da49 100644
--- a/src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs
+++ b/src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs
@@ -1131,7 +1131,7 @@ public virtual async Task FindDirtyAsync(object[] currentState, object[]
{
cancellationToken.ThrowIfCancellationRequested();
int[] props = await (TypeHelper.FindDirtyAsync(
- entityMetamodel.Properties, currentState, previousState, propertyColumnUpdateable, HasUninitializedLazyProperties(entity), session, cancellationToken)).ConfigureAwait(false);
+ entityMetamodel.Properties, currentState, previousState, propertyColumnUpdateable, session, cancellationToken)).ConfigureAwait(false);
if (props == null)
{
@@ -1148,7 +1148,7 @@ public virtual async Task FindModifiedAsync(object[] old, object[] curren
{
cancellationToken.ThrowIfCancellationRequested();
int[] props = await (TypeHelper.FindModifiedAsync(
- entityMetamodel.Properties, current, old, propertyColumnUpdateable, HasUninitializedLazyProperties(entity), session, cancellationToken)).ConfigureAwait(false);
+ entityMetamodel.Properties, current, old, propertyColumnUpdateable, session, cancellationToken)).ConfigureAwait(false);
if (props == null)
{
return null;
diff --git a/src/NHibernate/Async/Type/TypeHelper.cs b/src/NHibernate/Async/Type/TypeHelper.cs
index 4fc408ea24c..6208289b5b9 100644
--- a/src/NHibernate/Async/Type/TypeHelper.cs
+++ b/src/NHibernate/Async/Type/TypeHelper.cs
@@ -262,12 +262,39 @@ public static async Task ReplaceAssociationsAsync(object[] original, o
/// The session from which the dirty check request originated.
/// A cancellation token that can be used to cancel the work
/// Array containing indices of the dirty properties, or null if no properties considered dirty.
- public static async Task FindDirtyAsync(StandardProperty[] properties,
+ // Since 5.3
+ [Obsolete("Use overload without anyUninitializedProperties parameter instead")]
+ public static Task FindDirtyAsync(StandardProperty[] properties,
object[] currentState,
object[] previousState,
bool[][] includeColumns,
bool anyUninitializedProperties,
ISessionImplementor session, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ return FindDirtyAsync(properties, currentState, previousState, includeColumns, session, cancellationToken);
+ }
+
+ ///
+ /// Determine if any of the given field values are dirty, returning an array containing
+ /// indices of the dirty fields.
+ /// If it is determined that no fields are dirty, null is returned.
+ ///
+ /// The property definitions
+ /// The current state of the entity
+ /// The baseline state of the entity
+ /// Columns to be included in the dirty checking, per property
+ /// The session from which the dirty check request originated.
+ /// A cancellation token that can be used to cancel the work
+ /// Array containing indices of the dirty properties, or null if no properties considered dirty.
+ public static async Task FindDirtyAsync(StandardProperty[] properties,
+ object[] currentState,
+ object[] previousState,
+ bool[][] includeColumns,
+ ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
int[] results = null;
@@ -276,7 +303,7 @@ public static async Task FindDirtyAsync(StandardProperty[] properties,
for (int i = 0; i < span; i++)
{
- var dirty = await (DirtyAsync(properties, currentState, previousState, includeColumns, anyUninitializedProperties, session, i, cancellationToken)).ConfigureAwait(false);
+ var dirty = await (DirtyAsync(properties, currentState, previousState, includeColumns, session, i, cancellationToken)).ConfigureAwait(false);
if (dirty)
{
if (results == null)
@@ -298,14 +325,14 @@ public static async Task FindDirtyAsync(StandardProperty[] properties,
}
}
- private static async Task DirtyAsync(StandardProperty[] properties, object[] currentState, object[] previousState, bool[][] includeColumns, bool anyUninitializedProperties, ISessionImplementor session, int i, CancellationToken cancellationToken)
+ private static async Task DirtyAsync(StandardProperty[] properties, object[] currentState, object[] previousState, bool[][] includeColumns, ISessionImplementor session, int i, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (Equals(LazyPropertyInitializer.UnfetchedProperty, currentState[i]))
return false;
if (Equals(LazyPropertyInitializer.UnfetchedProperty, previousState[i]))
return true;
- return properties[i].IsDirtyCheckable(anyUninitializedProperties) &&
+ return properties[i].IsDirtyCheckable() &&
await (properties[i].Type.IsDirtyAsync(previousState[i], currentState[i], includeColumns[i], session, cancellationToken)).ConfigureAwait(false);
}
@@ -322,12 +349,39 @@ private static async Task DirtyAsync(StandardProperty[] properties, object
/// The session from which the dirty check request originated.
/// A cancellation token that can be used to cancel the work
/// Array containing indices of the modified properties, or null if no properties considered modified.
- public static async Task FindModifiedAsync(StandardProperty[] properties,
+ // Since 5.3
+ [Obsolete("Use the overload without anyUninitializedProperties parameter.")]
+ public static Task FindModifiedAsync(StandardProperty[] properties,
object[] currentState,
object[] previousState,
bool[][] includeColumns,
bool anyUninitializedProperties,
ISessionImplementor session, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return Task.FromCanceled(cancellationToken);
+ }
+ return FindModifiedAsync(properties, currentState, previousState, includeColumns, session, cancellationToken);
+ }
+
+ ///
+ /// Determine if any of the given field values are modified, returning an array containing
+ /// indices of the modified fields.
+ /// If it is determined that no fields are dirty, null is returned.
+ ///
+ /// The property definitions
+ /// The current state of the entity
+ /// The baseline state of the entity
+ /// Columns to be included in the mod checking, per property
+ /// The session from which the dirty check request originated.
+ /// A cancellation token that can be used to cancel the work
+ /// Array containing indices of the modified properties, or null if no properties considered modified.
+ public static async Task FindModifiedAsync(StandardProperty[] properties,
+ object[] currentState,
+ object[] previousState,
+ bool[][] includeColumns,
+ ISessionImplementor session, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
int[] results = null;
@@ -338,7 +392,7 @@ public static async Task FindModifiedAsync(StandardProperty[] properties,
{
bool dirty =
!Equals(LazyPropertyInitializer.UnfetchedProperty, currentState[i]) &&
- properties[i].IsDirtyCheckable(anyUninitializedProperties)
+ properties[i].IsDirtyCheckable()
&& await (properties[i].Type.IsModifiedAsync(previousState[i], currentState[i], includeColumns[i], session, cancellationToken)).ConfigureAwait(false);
if (dirty)
diff --git a/src/NHibernate/Bytecode/IBytecodeEnhancementMetadata.cs b/src/NHibernate/Bytecode/IBytecodeEnhancementMetadata.cs
index df7f58e4fd5..eebd202b490 100644
--- a/src/NHibernate/Bytecode/IBytecodeEnhancementMetadata.cs
+++ b/src/NHibernate/Bytecode/IBytecodeEnhancementMetadata.cs
@@ -40,11 +40,9 @@ public interface IBytecodeEnhancementMetadata
/// Build and inject an interceptor instance into the enhanced entity.
///
/// The entity into which built interceptor should be injected.
- /// Whether all lazy properties were unfetched or not.
/// The session to which the entity instance belongs.
/// The built and injected interceptor.
- // TODO: Remove lazyPropertiesAreUnfetched when interceptor will be injected on entity instantiation
- IFieldInterceptor InjectInterceptor(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session);
+ IFieldInterceptor InjectInterceptor(object entity, ISessionImplementor session);
///
/// Extract the field interceptor instance from the enhanced entity.
@@ -66,5 +64,12 @@ public interface IBytecodeEnhancementMetadata
/// The entity state from which to retrieve the uninitialized lazy properties.
/// The uninitialized property names.
ISet GetUninitializedLazyProperties(object[] entityState);
+
+ ///
+ /// Check whether the enhanced entity has any uninitialized lazy properties.
+ ///
+ /// The entity to check for uninitialized lazy properties.
+ /// Whether the enhanced entity has any uninitialized lazy properties.
+ bool HasAnyUninitializedLazyProperties(object entity);
}
}
diff --git a/src/NHibernate/Bytecode/LazyPropertiesMetadata.cs b/src/NHibernate/Bytecode/LazyPropertiesMetadata.cs
index d27ec518b3b..59b85ea6810 100644
--- a/src/NHibernate/Bytecode/LazyPropertiesMetadata.cs
+++ b/src/NHibernate/Bytecode/LazyPropertiesMetadata.cs
@@ -18,24 +18,43 @@ public class LazyPropertiesMetadata
{
public static LazyPropertiesMetadata NonEnhanced(string entityName)
{
- return new LazyPropertiesMetadata(entityName, null);
+ return new LazyPropertiesMetadata(entityName, null, null);
}
public static LazyPropertiesMetadata From(
string entityName,
IEnumerable lazyPropertyDescriptors)
{
- // TODO: port lazy fetch groups
+ var lazyProperties = new Dictionary();
+ var fetchGroups = new Dictionary>();
+ var mutableFetchGroups = new Dictionary>();
+
+ foreach (var propertyDescriptor in lazyPropertyDescriptors)
+ {
+ lazyProperties.Add(propertyDescriptor.Name, propertyDescriptor);
+ if (!mutableFetchGroups.TryGetValue(propertyDescriptor.FetchGroupName, out var fetchGroup))
+ {
+ fetchGroup = new HashSet();
+ mutableFetchGroups.Add(propertyDescriptor.FetchGroupName, fetchGroup);
+ fetchGroups.Add(propertyDescriptor.FetchGroupName, new ReadOnlySet(fetchGroup));
+ }
+
+ fetchGroup.Add(propertyDescriptor.Name);
+ }
+
return new LazyPropertiesMetadata(
entityName,
- lazyPropertyDescriptors.ToDictionary(o => o.Name));
+ lazyProperties,
+ fetchGroups);
}
private readonly IDictionary _lazyPropertyDescriptors;
+ private readonly IDictionary> _fetchGroups;
public LazyPropertiesMetadata(
string entityName,
- IDictionary lazyPropertyDescriptors)
+ IDictionary lazyPropertyDescriptors,
+ IDictionary> fetchGroups)
{
EntityName = entityName;
_lazyPropertyDescriptors = lazyPropertyDescriptors;
@@ -43,7 +62,11 @@ public LazyPropertiesMetadata(
LazyPropertyNames = HasLazyProperties
? new ReadOnlySet(new HashSet(_lazyPropertyDescriptors.Keys))
: CollectionHelper.EmptySet();
- // TODO: port lazy fetch groups
+
+ _fetchGroups = fetchGroups;
+ FetchGroupNames = _fetchGroups?.Count > 0
+ ? new ReadOnlySet(new HashSet(_fetchGroups.Keys))
+ : CollectionHelper.EmptySet();
}
public string EntityName { get; }
@@ -52,6 +75,8 @@ public LazyPropertiesMetadata(
public ISet LazyPropertyNames { get; }
+ public ISet FetchGroupNames { get; }
+
public IEnumerable LazyPropertyDescriptors =>
_lazyPropertyDescriptors?.Values ?? Enumerable.Empty();
@@ -70,5 +95,29 @@ public LazyPropertyDescriptor GetLazyPropertyDescriptor(string propertyName)
return descriptor;
}
+
+ public string GetFetchGroupName(string propertyName)
+ {
+ return GetLazyPropertyDescriptor(propertyName).FetchGroupName;
+ }
+
+ public ISet GetPropertiesInFetchGroup(string groupName)
+ {
+ if (!_fetchGroups.TryGetValue(groupName, out var properties))
+ {
+ throw new InvalidOperationException(
+ $"Fetch group {groupName} does not exist for entity {EntityName}");
+ }
+
+ return properties;
+ }
+
+ public IEnumerable GetFetchGroupPropertyDescriptors(string groupName)
+ {
+ foreach (var propertyName in GetPropertiesInFetchGroup(groupName))
+ {
+ yield return GetLazyPropertyDescriptor(propertyName);
+ }
+ }
}
}
diff --git a/src/NHibernate/Bytecode/LazyPropertyDescriptor.cs b/src/NHibernate/Bytecode/LazyPropertyDescriptor.cs
index a0640ef9470..1352e605831 100644
--- a/src/NHibernate/Bytecode/LazyPropertyDescriptor.cs
+++ b/src/NHibernate/Bytecode/LazyPropertyDescriptor.cs
@@ -20,13 +20,16 @@ public static LazyPropertyDescriptor From(
int propertyIndex,
int lazyIndex)
{
- // TODO: port lazy fetch groups
+ var fetchGroupName = string.IsNullOrEmpty(property.LazyGroup)
+ ? "DEFAULT"
+ : property.LazyGroup;
return new LazyPropertyDescriptor(
propertyIndex,
lazyIndex,
property.Name,
- property.Type
+ property.Type,
+ fetchGroupName
);
}
@@ -34,7 +37,8 @@ private LazyPropertyDescriptor(
int propertyIndex,
int lazyIndex,
string name,
- IType type)
+ IType type,
+ string fetchGroupName)
{
if (propertyIndex < lazyIndex)
{
@@ -45,6 +49,7 @@ private LazyPropertyDescriptor(
LazyIndex = lazyIndex;
Name = name;
Type = type;
+ FetchGroupName = fetchGroupName;
}
///
@@ -66,5 +71,10 @@ private LazyPropertyDescriptor(
/// Access to the property's type
///
public IType Type { get; }
+
+ ///
+ /// Access to the name of the fetch group to which the property belongs
+ ///
+ public string FetchGroupName { get; }
}
}
diff --git a/src/NHibernate/Cache/Entry/CacheEntry.cs b/src/NHibernate/Cache/Entry/CacheEntry.cs
index 368bb1ed37c..fb431ea489e 100644
--- a/src/NHibernate/Cache/Entry/CacheEntry.cs
+++ b/src/NHibernate/Cache/Entry/CacheEntry.cs
@@ -34,6 +34,8 @@ public CacheEntry(object[] state, IEntityPersister persister, bool unfetched, ob
Version = version;
}
+ // Since 5.3
+ [Obsolete("Use the overload without unfetched parameter instead.")]
public static CacheEntry Create(object[] state, IEntityPersister persister, bool unfetched, object version,
ISessionImplementor session, object owner)
{
@@ -52,6 +54,23 @@ public static CacheEntry Create(object[] state, IEntityPersister persister, bool
};
}
+ public static CacheEntry Create(object[] state, IEntityPersister persister, object version,
+ ISessionImplementor session, object owner)
+ {
+ return new CacheEntry
+ {
+ //disassembled state gets put in a new array (we write to cache by value!)
+ DisassembledState = TypeHelper.Disassemble(
+ state,
+ persister.PropertyTypes,
+ persister.IsLazyPropertiesCacheable ? null : persister.PropertyLaziness,
+ session,
+ owner),
+ Subclass = persister.EntityName,
+ Version = version
+ };
+ }
+
// 6.0 TODO convert to auto-property
[DataMember]
public object Version
@@ -68,8 +87,9 @@ public string Subclass
set => subclass = value;
}
- // 6.0 TODO convert to auto-property
[DataMember]
+ // Since 5.3
+ [Obsolete("This property is not used and will be removed in a future version.")]
public bool AreLazyPropertiesUnfetched
{
get => lazyPropertiesAreUnfetched;
diff --git a/src/NHibernate/Cache/Entry/StructuredCacheEntry.cs b/src/NHibernate/Cache/Entry/StructuredCacheEntry.cs
index bc9bf20372a..1772d1f74ee 100644
--- a/src/NHibernate/Cache/Entry/StructuredCacheEntry.cs
+++ b/src/NHibernate/Cache/Entry/StructuredCacheEntry.cs
@@ -18,7 +18,6 @@ public StructuredCacheEntry(IEntityPersister persister)
public object Destructure(object item, ISessionFactoryImplementor factory)
{
IDictionary map = (IDictionary)item;
- bool lazyPropertiesUnfetched = ((bool)map["_lazyPropertiesUnfetched"]);
string subclass = (string)map["_subclass"];
object version = map["_version"];
IEntityPersister subclassPersister = factory.GetEntityPersister(subclass);
@@ -32,8 +31,7 @@ public object Destructure(object item, ISessionFactoryImplementor factory)
{
Subclass = subclass,
DisassembledState = state,
- Version = version,
- AreLazyPropertiesUnfetched = lazyPropertiesUnfetched
+ Version = version
};
}
@@ -44,7 +42,6 @@ public object Structure(object item)
IDictionary map = new Hashtable(names.Length + 2);
map["_subclass"] = entry.Subclass;
map["_version"] = entry.Version;
- map["_lazyPropertiesUnfetched"] = entry.AreLazyPropertiesUnfetched;
for (int i = 0; i < names.Length; i++)
{
map[names[i]] = entry.DisassembledState[i];
diff --git a/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs b/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs
index 4c18d55f26d..aec0e5e457e 100644
--- a/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs
+++ b/src/NHibernate/Cfg/MappingSchema/Hbm.generated.cs
@@ -982,6 +982,10 @@ public partial class HbmProperty {
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
public bool lazy;
+
+ ///
+ [System.Xml.Serialization.XmlAttributeAttribute("lazy-group")]
+ public string lazygroup;
///
[System.Xml.Serialization.XmlAttributeAttribute()]
@@ -2234,6 +2238,10 @@ public partial class HbmComponent {
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(false)]
public bool lazy;
+
+ ///
+ [System.Xml.Serialization.XmlAttributeAttribute("lazy-group")]
+ public string lazygroup;
///
[System.Xml.Serialization.XmlAttributeAttribute("optimistic-lock")]
diff --git a/src/NHibernate/Cfg/MappingSchema/HbmProperty.cs b/src/NHibernate/Cfg/MappingSchema/HbmProperty.cs
index f99dcbe2ce2..04c0a1f0611 100644
--- a/src/NHibernate/Cfg/MappingSchema/HbmProperty.cs
+++ b/src/NHibernate/Cfg/MappingSchema/HbmProperty.cs
@@ -20,6 +20,8 @@ public bool IsLazyProperty
get { return lazy; }
}
+ public string FetchGroup => lazygroup;
+
public string Access
{
get { return access; }
diff --git a/src/NHibernate/Cfg/MappingSchema/IEntityPropertyMapping.cs b/src/NHibernate/Cfg/MappingSchema/IEntityPropertyMapping.cs
index 675de6155ac..ccdc34bd332 100644
--- a/src/NHibernate/Cfg/MappingSchema/IEntityPropertyMapping.cs
+++ b/src/NHibernate/Cfg/MappingSchema/IEntityPropertyMapping.cs
@@ -7,4 +7,23 @@ public interface IEntityPropertyMapping: IDecoratable
bool OptimisticLock { get; }
bool IsLazyProperty { get; }
}
-}
\ No newline at end of file
+
+ internal static class EntityPropertyMappingExtensiosns
+ {
+ // 6.0 TODO: Move to IEntityPropertyMapping as a property
+ public static string GetLazyGroup(this IEntityPropertyMapping mapping)
+ {
+ if (mapping is HbmProperty property)
+ {
+ return property.lazygroup;
+ }
+
+ if (mapping is HbmComponent component)
+ {
+ return component.lazygroup;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs b/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs
index b2c01800172..a5ad33b51bb 100644
--- a/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs
+++ b/src/NHibernate/Cfg/XmlHbmBinding/PropertiesBinder.cs
@@ -419,6 +419,7 @@ private Property CreateProperty(IEntityPropertyMapping propertyMapping, string p
PropertyAccessorName = propertyAccessorName,
Value = value,
IsLazy = propertyMapping.IsLazyProperty,
+ LazyGroup = propertyMapping.GetLazyGroup(),
IsOptimisticLocked = propertyMapping.OptimisticLock,
MetaAttributes = GetMetas(propertyMapping, inheritedMetas)
};
diff --git a/src/NHibernate/Engine/EntityEntry.cs b/src/NHibernate/Engine/EntityEntry.cs
index 1ae1daaa16d..ac702632adb 100644
--- a/src/NHibernate/Engine/EntityEntry.cs
+++ b/src/NHibernate/Engine/EntityEntry.cs
@@ -45,9 +45,31 @@ public sealed class EntityEntry
/// The that is responsible for this Entity.
///
///
+ // Since 5.3
+ [Obsolete("Use the constructor without lazyPropertiesAreUnfetched parameter")]
+ internal EntityEntry(Status status, object[] loadedState, object rowId, object id, object version, LockMode lockMode,
+ bool existsInDatabase, IEntityPersister persister,
+ bool disableVersionIncrement, bool lazyPropertiesAreUnfetched)
+ :this(status, loadedState, rowId, id, version, lockMode, existsInDatabase, persister, disableVersionIncrement)
+ {
+ loadedWithLazyPropertiesUnfetched = lazyPropertiesAreUnfetched;
+ }
+
+ ///
+ /// Initializes a new instance of EntityEntry.
+ ///
+ /// The current of the Entity.
+ /// The snapshot of the Entity's state when it was loaded.
+ ///
+ /// The identifier of the Entity in the database.
+ /// The version of the Entity.
+ /// The for the Entity.
+ /// A boolean indicating if the Entity exists in the database.
+ /// The that is responsible for this Entity.
+ ///
internal EntityEntry(Status status, object[] loadedState, object rowId, object id, object version, LockMode lockMode,
bool existsInDatabase, IEntityPersister persister,
- bool disableVersionIncrement, bool lazyPropertiesAreUnfetched)
+ bool disableVersionIncrement)
{
this.status = status;
this.previousStatus = null;
@@ -59,7 +81,6 @@ internal EntityEntry(Status status, object[] loadedState, object rowId, object i
this.version = version;
this.lockMode = lockMode;
isBeingReplicated = disableVersionIncrement;
- loadedWithLazyPropertiesUnfetched = lazyPropertiesAreUnfetched;
this.persister = persister;
entityName = persister == null ? null : persister.EntityName;
}
@@ -180,6 +201,8 @@ public object RowId
get { return rowId; }
}
+ // Since 5.3
+ [Obsolete("This property is not used and will be removed in a future version.")]
public bool LoadedWithLazyPropertiesUnfetched
{
get { return loadedWithLazyPropertiesUnfetched; }
diff --git a/src/NHibernate/Engine/IPersistenceContext.cs b/src/NHibernate/Engine/IPersistenceContext.cs
index 9e3e4cf70ce..3544bc2d3a9 100644
--- a/src/NHibernate/Engine/IPersistenceContext.cs
+++ b/src/NHibernate/Engine/IPersistenceContext.cs
@@ -1,7 +1,10 @@
+using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using NHibernate.Collection;
using NHibernate.Engine.Loading;
+using NHibernate.Intercept;
using NHibernate.Persister.Collection;
using NHibernate.Persister.Entity;
using NHibernate.Proxy;
@@ -160,6 +163,8 @@ public partial interface IPersistenceContext
CollectionEntry GetCollectionEntry(IPersistentCollection coll);
/// Adds an entity to the internal caches.
+ // Since 5.3
+ [Obsolete("Use the AddEntity extension method instead")]
EntityEntry AddEntity(object entity, Status status, object[] loadedState, EntityKey entityKey, object version,
LockMode lockMode, bool existsInDatabase, IEntityPersister persister,
bool disableVersionIncrement, bool lazyPropertiesAreUnfetched);
@@ -168,6 +173,8 @@ EntityEntry AddEntity(object entity, Status status, object[] loadedState, Entity
/// Generates an appropriate EntityEntry instance and adds it
/// to the event source's internal caches.
///
+ // Since 5.3
+ [Obsolete("Use the AddEntry extension method instead")]
EntityEntry AddEntry(object entity, Status status, object[] loadedState, object rowId, object id, object version,
LockMode lockMode, bool existsInDatabase, IEntityPersister persister, bool disableVersionIncrement,
bool lazyPropertiesAreUnfetched);
@@ -409,4 +416,97 @@ EntityEntry AddEntry(object entity, Status status, object[] loadedState, object
/// The child.
void RemoveChildParent(object child);
}
+
+ public static class PersistenceContextExtensions
+ {
+ /// Adds an entity to the internal caches.
+ public static EntityEntry AddEntity(
+ this IPersistenceContext context,
+ object entity,
+ Status status,
+ object[] loadedState,
+ EntityKey entityKey,
+ object version,
+ LockMode lockMode,
+ bool existsInDatabase,
+ IEntityPersister persister,
+ bool disableVersionIncrement)
+ {
+ if (context is StatefulPersistenceContext statefulPersistence)
+ {
+ return statefulPersistence.AddEntity(
+ entity,
+ status,
+ loadedState,
+ entityKey,
+ version,
+ lockMode,
+ existsInDatabase,
+ persister,
+ disableVersionIncrement);
+ }
+
+#pragma warning disable 618
+ return context.AddEntity(
+ entity,
+ status,
+ loadedState,
+ entityKey,
+ version,
+ lockMode,
+ existsInDatabase,
+ persister,
+ disableVersionIncrement,
+ loadedState?.Any(o => o == LazyPropertyInitializer.UnfetchedProperty) == true);
+#pragma warning restore 618
+ }
+
+ ///
+ /// Generates an appropriate EntityEntry instance and adds it
+ /// to the event source's internal caches.
+ ///
+ public static EntityEntry AddEntry(
+ this IPersistenceContext context,
+ object entity,
+ Status status,
+ object[] loadedState,
+ object rowId,
+ object id,
+ object version,
+ LockMode lockMode,
+ bool existsInDatabase,
+ IEntityPersister persister,
+ bool disableVersionIncrement)
+ {
+ if (context is StatefulPersistenceContext statefulPersistence)
+ {
+ return statefulPersistence.AddEntry(
+ entity,
+ status,
+ loadedState,
+ rowId,
+ id,
+ version,
+ lockMode,
+ existsInDatabase,
+ persister,
+ disableVersionIncrement);
+ }
+
+#pragma warning disable 618
+ return context.AddEntry(
+ entity,
+ status,
+ loadedState,
+ rowId,
+ id,
+ version,
+ lockMode,
+ existsInDatabase,
+ persister,
+ disableVersionIncrement,
+ loadedState?.Any(o => o == LazyPropertyInitializer.UnfetchedProperty) == true);
+#pragma warning restore 618
+ }
+ }
}
diff --git a/src/NHibernate/Engine/StatefulPersistenceContext.cs b/src/NHibernate/Engine/StatefulPersistenceContext.cs
index 0a2bcfe314b..021bf96bd23 100644
--- a/src/NHibernate/Engine/StatefulPersistenceContext.cs
+++ b/src/NHibernate/Engine/StatefulPersistenceContext.cs
@@ -513,6 +513,8 @@ public CollectionEntry GetCollectionEntry(IPersistentCollection coll)
}
/// Adds an entity to the internal caches.
+ // Since v5.3
+ [Obsolete("Use overload without lazyPropertiesAreUnfetched parameter")]
public EntityEntry AddEntity(object entity, Status status, object[] loadedState, EntityKey entityKey, object version,
LockMode lockMode, bool existsInDatabase, IEntityPersister persister,
bool disableVersionIncrement, bool lazyPropertiesAreUnfetched)
@@ -522,10 +524,22 @@ public EntityEntry AddEntity(object entity, Status status, object[] loadedState,
return AddEntry(entity, status, loadedState, null, entityKey.Identifier, version, lockMode, existsInDatabase, persister, disableVersionIncrement, lazyPropertiesAreUnfetched);
}
+ /// Adds an entity to the internal caches.
+ public EntityEntry AddEntity(object entity, Status status, object[] loadedState, EntityKey entityKey, object version,
+ LockMode lockMode, bool existsInDatabase, IEntityPersister persister,
+ bool disableVersionIncrement)
+ {
+ AddEntity(entityKey, entity);
+
+ return AddEntry(entity, status, loadedState, null, entityKey.Identifier, version, lockMode, existsInDatabase, persister, disableVersionIncrement);
+ }
+
///
/// Generates an appropriate EntityEntry instance and adds it
/// to the event source's internal caches.
///
+ // Since v5.3
+ [Obsolete("Use overload without lazyPropertiesAreUnfetched parameter")]
public EntityEntry AddEntry(object entity, Status status, object[] loadedState, object rowId, object id,
object version, LockMode lockMode, bool existsInDatabase, IEntityPersister persister,
bool disableVersionIncrement, bool lazyPropertiesAreUnfetched)
@@ -539,6 +553,23 @@ public EntityEntry AddEntry(object entity, Status status, object[] loadedState,
return e;
}
+ ///
+ /// Generates an appropriate EntityEntry instance and adds it
+ /// to the event source's internal caches.
+ ///
+ public EntityEntry AddEntry(object entity, Status status, object[] loadedState, object rowId, object id,
+ object version, LockMode lockMode, bool existsInDatabase, IEntityPersister persister,
+ bool disableVersionIncrement)
+ {
+ EntityEntry e =
+ new EntityEntry(status, loadedState, rowId, id, version, lockMode, existsInDatabase, persister,
+ disableVersionIncrement);
+ entityEntries[entity] = e;
+
+ SetHasNonReadOnlyEnties(status);
+ return e;
+ }
+
/// Is the given collection associated with this persistence context?
public bool ContainsCollection(IPersistentCollection collection)
{
@@ -1364,8 +1395,7 @@ public void ReplaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, object gene
var newKey = Session.GenerateEntityKey(generatedId, oldEntry.Persister);
AddEntity(newKey, entity);
AddEntry(entity, oldEntry.Status, oldEntry.LoadedState, oldEntry.RowId, generatedId, oldEntry.Version,
- oldEntry.LockMode, oldEntry.ExistsInDatabase, oldEntry.Persister, oldEntry.IsBeingReplicated,
- oldEntry.LoadedWithLazyPropertiesUnfetched);
+ oldEntry.LockMode, oldEntry.ExistsInDatabase, oldEntry.Persister, oldEntry.IsBeingReplicated);
}
public bool IsLoadFinished
diff --git a/src/NHibernate/Engine/TwoPhaseLoad.cs b/src/NHibernate/Engine/TwoPhaseLoad.cs
index 4dc54578f10..eee2a5a4c06 100644
--- a/src/NHibernate/Engine/TwoPhaseLoad.cs
+++ b/src/NHibernate/Engine/TwoPhaseLoad.cs
@@ -22,7 +22,7 @@ namespace NHibernate.Engine
public static partial class TwoPhaseLoad
{
private static readonly INHibernateLogger log = NHibernateLogger.For(typeof(TwoPhaseLoad));
-
+
///
/// Register the "hydrated" state of an entity instance, after the first step of 2-phase loading.
///
@@ -30,6 +30,8 @@ public static partial class TwoPhaseLoad
/// to resolve any associations yet, because there might be other entities waiting to be
/// read from the JDBC result set we are currently processing
///
+ // Since 5.3
+ [Obsolete("Use the overload without lazyPropertiesAreUnfetched parameter instead")]
public static void PostHydrate(IEntityPersister persister, object id, object[] values, object rowId, object obj, LockMode lockMode, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
{
object version = Versioning.GetVersion(values, persister);
@@ -41,7 +43,26 @@ public static void PostHydrate(IEntityPersister persister, object id, object[] v
log.Debug("Version: {0}", versionStr);
}
}
-
+
+ ///
+ /// Register the "hydrated" state of an entity instance, after the first step of 2-phase loading.
+ ///
+ /// Add the "hydrated state" (an array) of an uninitialized entity to the session. We don't try
+ /// to resolve any associations yet, because there might be other entities waiting to be
+ /// read from the JDBC result set we are currently processing
+ ///
+ public static void PostHydrate(IEntityPersister persister, object id, object[] values, object rowId, object obj, LockMode lockMode, ISessionImplementor session)
+ {
+ object version = Versioning.GetVersion(values, persister);
+ session.PersistenceContext.AddEntry(obj, Status.Loading, values, rowId, id, version, lockMode, true, persister, false);
+
+ if (log.IsDebugEnabled() && version != null)
+ {
+ System.String versionStr = persister.IsVersioned ? persister.VersionType.ToLoggableString(version, session.Factory) : "null";
+ log.Debug("Version: {0}", versionStr);
+ }
+ }
+
///
/// Perform the second step of 2-phase load. Fully initialize the entity instance.
/// After processing a JDBC result set, we "resolve" all the associations
@@ -132,7 +153,7 @@ internal static void InitializeEntity(object entity, bool readOnly, ISessionImpl
object version = Versioning.GetVersion(hydratedState, persister);
CacheEntry entry =
- CacheEntry.Create(hydratedState, persister, entityEntry.LoadedWithLazyPropertiesUnfetched, version, session, entity);
+ CacheEntry.Create(hydratedState, persister, version, session, entity);
CacheKey cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName);
if (cacheBatchingHandler != null && persister.IsBatchLoadable)
@@ -192,7 +213,7 @@ internal static void InitializeEntity(object entity, bool readOnly, ISessionImpl
persistenceContext.SetEntryStatus(entityEntry, Status.Loaded);
}
- persister.AfterInitialize(entity, entityEntry.LoadedWithLazyPropertiesUnfetched, session);
+ persister.AfterInitialize(entity, session);
if (session.IsEventSource)
{
@@ -219,7 +240,8 @@ internal static void InitializeEntity(object entity, bool readOnly, ISessionImpl
private static bool UseMinimalPuts(ISessionImplementor session, EntityEntry entityEntry)
{
return (session.Factory.Settings.IsMinimalPutsEnabled && session.CacheMode != CacheMode.Refresh)
- || (entityEntry.Persister.HasLazyProperties && entityEntry.LoadedWithLazyPropertiesUnfetched && entityEntry.Persister.IsLazyPropertiesCacheable);
+ // Use minimal puts also for cacheable lazy properties in order to avoid sending large objects (e.g. an image)
+ || (entityEntry.Persister.HasLazyProperties && entityEntry.Persister.IsLazyPropertiesCacheable);
}
///
@@ -228,14 +250,34 @@ private static bool UseMinimalPuts(ISessionImplementor session, EntityEntry enti
/// Create a "temporary" entry for a newly instantiated entity. The entity is uninitialized,
/// but we need the mapping from id to instance in order to guarantee uniqueness.
///
+ // Since 5.3
+ [Obsolete("Use the overload without the lazyPropertiesAreUnfetched parameter")]
public static void AddUninitializedEntity(EntityKey key, object obj, IEntityPersister persister, LockMode lockMode, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
{
session.PersistenceContext.AddEntity(obj, Status.Loading, null, key, null, lockMode, true, persister, false, lazyPropertiesAreUnfetched);
}
+ ///
+ /// Add an uninitialized instance of an entity class, as a placeholder to ensure object
+ /// identity. Must be called before postHydrate() .
+ /// Create a "temporary" entry for a newly instantiated entity. The entity is uninitialized,
+ /// but we need the mapping from id to instance in order to guarantee uniqueness.
+ ///
+ public static void AddUninitializedEntity(EntityKey key, object obj, IEntityPersister persister, LockMode lockMode, ISessionImplementor session)
+ {
+ session.PersistenceContext.AddEntity(obj, Status.Loading, null, key, null, lockMode, true, persister, false);
+ }
+
+ // Since 5.3
+ [Obsolete("Use the overload without the lazyPropertiesAreUnfetched parameter")]
public static void AddUninitializedCachedEntity(EntityKey key, object obj, IEntityPersister persister, LockMode lockMode, bool lazyPropertiesAreUnfetched, object version, ISessionImplementor session)
{
session.PersistenceContext.AddEntity(obj, Status.Loading, null, key, version, lockMode, true, persister, false, lazyPropertiesAreUnfetched);
}
+
+ public static void AddUninitializedCachedEntity(EntityKey key, object obj, IEntityPersister persister, LockMode lockMode, object version, ISessionImplementor session)
+ {
+ session.PersistenceContext.AddEntity(obj, Status.Loading, null, key, version, lockMode, true, persister, false);
+ }
}
}
diff --git a/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs b/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs
index 81b37054009..ee81e52e18a 100644
--- a/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs
+++ b/src/NHibernate/Event/Default/AbstractReassociateEventListener.cs
@@ -51,8 +51,7 @@ protected EntityEntry Reassociate(AbstractEvent @event, object entity, object id
LockMode.None,
true,
persister,
- false,
- true);
+ false);
new OnLockVisitor(source, id, entity).Process(entity, persister);
diff --git a/src/NHibernate/Event/Default/AbstractSaveEventListener.cs b/src/NHibernate/Event/Default/AbstractSaveEventListener.cs
index d3dadbb5dc9..f4fe501ab2e 100644
--- a/src/NHibernate/Event/Default/AbstractSaveEventListener.cs
+++ b/src/NHibernate/Event/Default/AbstractSaveEventListener.cs
@@ -216,7 +216,7 @@ protected virtual object PerformSaveOrReplicate(object entity, EntityKey key, IE
// Put a placeholder in entries, so we don't recurse back and try to save() the
// same object again. QUESTION: should this be done before onSave() is called?
// likewise, should it be done before onUpdate()?
- source.PersistenceContext.AddEntry(entity, Status.Saving, null, null, id, null, LockMode.Write, useIdentityColumn, persister, false, false);
+ source.PersistenceContext.AddEntry(entity, Status.Saving, null, null, id, null, LockMode.Write, useIdentityColumn, persister, false);
CascadeBeforeSave(source, persister, entity, anything);
@@ -278,8 +278,7 @@ protected virtual object PerformSaveOrReplicate(object entity, EntityKey key, IE
LockMode.Write,
useIdentityColumn,
persister,
- VersionIncrementDisabled,
- false);
+ VersionIncrementDisabled);
//source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );
if (!useIdentityColumn)
@@ -299,7 +298,7 @@ private void MarkInterceptorDirty(object entity, IEntityPersister persister, IEv
if (persister.IsInstrumented)
{
var interceptor = persister.EntityMetamodel.BytecodeEnhancementMetadata
- .InjectInterceptor(entity, false, source);
+ .InjectInterceptor(entity, source);
interceptor?.MarkDirty();
}
}
diff --git a/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs b/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs
index c4f699f863f..13f9a2e20a1 100644
--- a/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs
+++ b/src/NHibernate/Event/Default/DefaultDeleteEventListener.cs
@@ -81,7 +81,6 @@ public virtual void OnDelete(DeleteEvent @event, ISet transientEntities)
LockMode.None,
true,
persister,
- false,
false);
}
else
diff --git a/src/NHibernate/Event/Default/DefaultLoadEventListener.cs b/src/NHibernate/Event/Default/DefaultLoadEventListener.cs
index 54445a9eeb0..036d9eaf59e 100644
--- a/src/NHibernate/Event/Default/DefaultLoadEventListener.cs
+++ b/src/NHibernate/Event/Default/DefaultLoadEventListener.cs
@@ -505,7 +505,7 @@ private object AssembleCacheEntry(CacheEntry entry, object id, IEntityPersister
// make it circular-reference safe
EntityKey entityKey = session.GenerateEntityKey(id, subclassPersister);
- TwoPhaseLoad.AddUninitializedCachedEntity(entityKey, result, subclassPersister, LockMode.None, entry.AreLazyPropertiesUnfetched, entry.Version, session);
+ TwoPhaseLoad.AddUninitializedCachedEntity(entityKey, result, subclassPersister, LockMode.None, entry.Version, session);
IType[] types = subclassPersister.PropertyTypes;
object[] values = entry.Assemble(result, id, subclassPersister, session.Interceptor, session); // intializes result by side-effect
@@ -543,10 +543,9 @@ private object AssembleCacheEntry(CacheEntry entry, object id, IEntityPersister
LockMode.None,
true,
subclassPersister,
- false,
- entry.AreLazyPropertiesUnfetched);
+ false);
- subclassPersister.AfterInitialize(result, entry.AreLazyPropertiesUnfetched, session);
+ subclassPersister.AfterInitialize(result, session);
persistenceContext.InitializeNonLazyCollections();
// upgrade the lock if necessary:
//lock(result, lockMode);
diff --git a/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs b/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs
index b274eb929eb..5fa2dc26de7 100644
--- a/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs
+++ b/src/NHibernate/Event/Default/DefaultReplicateEventListener.cs
@@ -119,8 +119,7 @@ private void PerformReplication(object entity, object id, object version, IEntit
LockMode.None,
true,
persister,
- true,
- false);
+ true);
CascadeAfterReplicate(entity, persister, replicationMode, source);
}
diff --git a/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs b/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs
index ac654e6afe2..897793a8180 100644
--- a/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs
+++ b/src/NHibernate/Event/Default/DefaultSaveOrUpdateEventListener.cs
@@ -262,8 +262,7 @@ protected virtual void PerformUpdate(SaveOrUpdateEvent @event, object entity, IE
LockMode.None,
true,
persister,
- false,
- true);
+ false);
//persister.AfterReassociate(entity, source); TODO H3.2 not ported
diff --git a/src/NHibernate/Intercept/AbstractFieldInterceptor.cs b/src/NHibernate/Intercept/AbstractFieldInterceptor.cs
index 515381a17fe..87ede553e0a 100644
--- a/src/NHibernate/Intercept/AbstractFieldInterceptor.cs
+++ b/src/NHibernate/Intercept/AbstractFieldInterceptor.cs
@@ -100,13 +100,6 @@ public object Intercept(object target, string fieldName, object value)
public object Intercept(object target, string fieldName, object value, bool setter)
{
- // NH Specific: Hibernate only deals with lazy properties here, we deal with
- // both lazy properties and with no-proxy.
- if (initializing)
- {
- return InvokeImplementation;
- }
-
if (setter)
{
if (IsUninitializedProperty(fieldName))
@@ -128,6 +121,13 @@ public object Intercept(object target, string fieldName, object value, bool sett
return value;
}
+ // NH Specific: Hibernate only deals with lazy properties here, we deal with
+ // both lazy properties and with no-proxy.
+ if (initializing)
+ {
+ return InvokeImplementation;
+ }
+
if (IsInitializedField(fieldName))
{
return value;
@@ -197,8 +197,7 @@ private object InitializeField(string fieldName, object target)
{
initializing = false;
}
- uninitializedFields = null; //let's assume that there is only one lazy fetch group, for now!
- uninitializedFieldsReadOnly = null;
+
return result;
}
diff --git a/src/NHibernate/Intercept/IFieldInterceptor.cs b/src/NHibernate/Intercept/IFieldInterceptor.cs
index 4fd38f85aa4..111e257dc60 100644
--- a/src/NHibernate/Intercept/IFieldInterceptor.cs
+++ b/src/NHibernate/Intercept/IFieldInterceptor.cs
@@ -16,6 +16,8 @@ public interface IFieldInterceptor
ISessionImplementor Session { get; set; }
/// Is the entity to which we are bound completely initialized?
+ // Since 5.3
+ [Obsolete("This property is not used and will be removed in a future version.")]
bool IsInitialized { get;}
/// The the given field initialized for the entity to which we are bound?
@@ -51,7 +53,9 @@ internal static ISet GetUninitializedFields(this IFieldInterceptor inter
return fieldInterceptor.GetUninitializedFields();
}
+#pragma warning disable 618
if (interceptor.IsInitialized)
+#pragma warning restore 618
{
return CollectionHelper.EmptySet();
}
diff --git a/src/NHibernate/Loader/Loader.cs b/src/NHibernate/Loader/Loader.cs
index b79f8a7805d..a89c9b9b007 100644
--- a/src/NHibernate/Loader/Loader.cs
+++ b/src/NHibernate/Loader/Loader.cs
@@ -1150,15 +1150,10 @@ private void UpdateLazyPropertiesFromResultSet(DbDataReader rs, int i, object ob
EntityEntry entry, ILoadable rootPersister, ISessionImplementor session,
Action cacheBatchingHandler)
{
- if (!entry.LoadedWithLazyPropertiesUnfetched)
- {
- return; // All lazy properties were already loaded
- }
-
- var eagerPropertyFetch = IsEagerPropertyFetchEnabled(i);
+ var fetchAllProperties = IsEagerPropertyFetchEnabled(i);
var fetchLazyProperties = GetFetchLazyProperties(i);
- if (!eagerPropertyFetch && fetchLazyProperties == null)
+ if (!fetchAllProperties && fetchLazyProperties == null)
{
return; // No lazy properties were loaded
}
@@ -1191,11 +1186,18 @@ private void UpdateLazyPropertiesFromResultSet(DbDataReader rs, int i, object ob
? EntityAliases[i].SuffixedPropertyAliases
: GetSubclassEntityAliases(i, persister);
- if (!persister.InitializeLazyProperties(rs, id, obj, rootPersister, cols, updateLazyProperties, eagerPropertyFetch, session))
+ if (!persister.InitializeLazyProperties(rs, id, obj, rootPersister, cols, updateLazyProperties, fetchAllProperties, session))
{
return;
}
+ UpdateCacheForEntity(obj, id, entry, persister, session, cacheBatchingHandler);
+ }
+
+ internal static void UpdateCacheForEntity(
+ object obj, object id, EntityEntry entry, IEntityPersister persister, ISessionImplementor session,
+ Action cacheBatchingHandler)
+ {
if (entry.Status == Status.Loading || !persister.HasCache ||
!session.CacheMode.HasFlag(CacheMode.Put) || !persister.IsLazyPropertiesCacheable)
{
@@ -1210,7 +1212,7 @@ private void UpdateLazyPropertiesFromResultSet(DbDataReader rs, int i, object ob
var factory = session.Factory;
var state = persister.GetPropertyValues(obj);
var version = Versioning.GetVersion(state, persister);
- var cacheEntry = CacheEntry.Create(state, persister, entry.LoadedWithLazyPropertiesUnfetched, version, session, obj);
+ var cacheEntry = CacheEntry.Create(state, persister, version, session, obj);
var cacheKey = session.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName);
if (cacheBatchingHandler != null && persister.IsBatchLoadable)
@@ -1259,23 +1261,23 @@ private void LoadFromResultSet(DbDataReader rs, int i, object obj, string instan
Log.Debug("Initializing object from DataReader: {0}", MessageHelper.InfoString(persister, id));
}
- bool eagerPropertyFetch = IsEagerPropertyFetchEnabled(i);
+ bool fetchAllProperties = IsEagerPropertyFetchEnabled(i);
var eagerFetchProperties = GetFetchLazyProperties(i);
// add temp entry so that the next step is circular-reference
// safe - only needed because some types don't take proper
// advantage of two-phase-load (esp. components)
- TwoPhaseLoad.AddUninitializedEntity(key, obj, persister, lockMode, !eagerPropertyFetch, session);
+ TwoPhaseLoad.AddUninitializedEntity(key, obj, persister, lockMode, session);
string[][] cols = persister == rootPersister
? EntityAliases[i].SuffixedPropertyAliases
: GetSubclassEntityAliases(i, persister);
- object[] values = persister.Hydrate(rs, id, obj, cols, eagerFetchProperties, eagerPropertyFetch, session);
+ object[] values = persister.Hydrate(rs, id, obj, cols, eagerFetchProperties, fetchAllProperties, session);
object rowId = persister.HasRowId ? rs[EntityAliases[i].RowIdAlias] : null;
- TwoPhaseLoad.PostHydrate(persister, id, values, rowId, obj, lockMode, !eagerPropertyFetch, session);
+ TwoPhaseLoad.PostHydrate(persister, id, values, rowId, obj, lockMode, session);
}
private string[][] GetSubclassEntityAliases(int i, ILoadable persister)
diff --git a/src/NHibernate/Mapping/ByCode/IComponentMapper.cs b/src/NHibernate/Mapping/ByCode/IComponentMapper.cs
index 41a06406c05..a13d769b6cd 100644
--- a/src/NHibernate/Mapping/ByCode/IComponentMapper.cs
+++ b/src/NHibernate/Mapping/ByCode/IComponentMapper.cs
@@ -1,6 +1,8 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
+using NHibernate.Mapping.ByCode.Impl;
+using NHibernate.Mapping.ByCode.Impl.CustomizersImpl;
namespace NHibernate.Mapping.ByCode
{
@@ -31,4 +33,29 @@ public interface IComponentAttributesMapper : IEntityPropertyMapper
public interface IComponentMapper : IComponentAttributesMapper, IPropertyContainerMapper
{}
-}
\ No newline at end of file
+
+ public static class ComponentAttributesMapperExtensions
+ {
+ // 6.0 TODO: Move to IComponentAttributesMapper
+ public static void LazyGroup(this IComponentAttributesMapper mapper, string name)
+ {
+ if (mapper is ComponentMapper component)
+ {
+ component.LazyGroup(name);
+ }
+ }
+
+ // 6.0 TODO: Move to IComponentAttributesMapper
+ public static void LazyGroup(this IComponentAttributesMapper mapper, string name)
+ {
+ if (mapper is ComponentCustomizer component)
+ {
+ component.LazyGroup(name);
+ }
+ else if (mapper is ComponentElementCustomizer componentElement)
+ {
+ componentElement.LazyGroup(name);
+ }
+ }
+ }
+}
diff --git a/src/NHibernate/Mapping/ByCode/IPropertyMapper.cs b/src/NHibernate/Mapping/ByCode/IPropertyMapper.cs
index b552c7d55d5..be463579543 100644
--- a/src/NHibernate/Mapping/ByCode/IPropertyMapper.cs
+++ b/src/NHibernate/Mapping/ByCode/IPropertyMapper.cs
@@ -1,3 +1,4 @@
+using NHibernate.Mapping.ByCode.Impl;
using NHibernate.Type;
namespace NHibernate.Mapping.ByCode
@@ -23,4 +24,16 @@ public interface IPropertyMapper : IEntityPropertyMapper, IColumnsMapper
void Lazy(bool isLazy);
void Generated(PropertyGeneration generation);
}
+
+ public static class PropertyMapperExtensions
+ {
+ // 6.0 TODO: Move to IPropertyMapper
+ public static void FetchGroup(this IPropertyMapper mapper, string name)
+ {
+ if (mapper is PropertyMapper property)
+ {
+ property.FetchGroup(name);
+ }
+ }
+ }
}
diff --git a/src/NHibernate/Mapping/ByCode/Impl/ComponentMapper.cs b/src/NHibernate/Mapping/ByCode/Impl/ComponentMapper.cs
index c414b03e9f3..801da3689e2 100644
--- a/src/NHibernate/Mapping/ByCode/Impl/ComponentMapper.cs
+++ b/src/NHibernate/Mapping/ByCode/Impl/ComponentMapper.cs
@@ -70,6 +70,11 @@ public void Lazy(bool isLazy)
_component.lazy = isLazy;
}
+ public void LazyGroup(string name)
+ {
+ _component.lazygroup = name;
+ }
+
public void Unique(bool unique)
{
_component.unique = unique;
@@ -111,4 +116,4 @@ private IComponentParentMapper GetParentMapper(MemberInfo parent)
return _parentMapper = new ComponentParentMapper(_component.parent, parent);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentCustomizer.cs
index d6979a5cac4..470b5a7b020 100644
--- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentCustomizer.cs
+++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentCustomizer.cs
@@ -64,6 +64,11 @@ public void Lazy(bool isLazy)
AddCustomizer(m => m.Lazy(isLazy));
}
+ public void LazyGroup(string name)
+ {
+ AddCustomizer(m => m.LazyGroup(name));
+ }
+
public void Unique(bool unique)
{
AddCustomizer(m=>m.Unique(unique));
@@ -121,4 +126,4 @@ IModelExplicitDeclarationsHolder IConformistHoldersProvider.ExplicitDeclarations
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentElementCustomizer.cs b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentElementCustomizer.cs
index 11453438bb3..60c529de125 100644
--- a/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentElementCustomizer.cs
+++ b/src/NHibernate/Mapping/ByCode/Impl/CustomizersImpl/ComponentElementCustomizer.cs
@@ -65,6 +65,11 @@ public void Lazy(bool isLazy)
_customizersHolder.AddCustomizer(typeof (TComponent), (IComponentAttributesMapper x) => x.Lazy(isLazy));
}
+ public void LazyGroup(string name)
+ {
+ _customizersHolder.AddCustomizer(typeof(TComponent), x => x.LazyGroup(name));
+ }
+
public void Unique(bool unique)
{
_customizersHolder.AddCustomizer(typeof(TComponent), (IComponentAttributesMapper x) => x.Unique(unique));
@@ -173,4 +178,4 @@ public void OptimisticLock(bool takeInConsiderationForOptimisticLock)
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Mapping/ByCode/Impl/PropertyMapper.cs b/src/NHibernate/Mapping/ByCode/Impl/PropertyMapper.cs
index 6d590e36d3b..b95843319e9 100644
--- a/src/NHibernate/Mapping/ByCode/Impl/PropertyMapper.cs
+++ b/src/NHibernate/Mapping/ByCode/Impl/PropertyMapper.cs
@@ -241,6 +241,11 @@ public void Lazy(bool isLazy)
propertyMapping.lazy = isLazy;
}
+ public void FetchGroup(string name)
+ {
+ propertyMapping.lazygroup = name;
+ }
+
public void Generated(PropertyGeneration generation)
{
if (generation == null)
diff --git a/src/NHibernate/Mapping/Property.cs b/src/NHibernate/Mapping/Property.cs
index f02dd580574..1207d10f68a 100644
--- a/src/NHibernate/Mapping/Property.cs
+++ b/src/NHibernate/Mapping/Property.cs
@@ -264,6 +264,8 @@ public bool IsLazy
set { isLazy = value; }
}
+ public string LazyGroup { get; set; }
+
public virtual bool BackRef
{
get { return false; }
diff --git a/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs b/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs
index c741f7cc912..ef644bba041 100644
--- a/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs
+++ b/src/NHibernate/Persister/Entity/AbstractEntityPersister.cs
@@ -211,7 +211,8 @@ public virtual void BindValues(DbCommand ps)
private SqlString sqlVersionSelectString;
private SqlString sqlSnapshotSelectString;
- private SqlString sqlLazySelectString;
+ private SqlString sqlLazySelectString; // 6.0 TODO: Remove
+ private IDictionary _sqlLazySelectStringsByFetchGroup;
private SqlCommandInfo sqlIdentityInsertString;
private SqlCommandInfo sqlUpdateByRowIdString;
@@ -604,11 +605,17 @@ protected SqlString SQLSnapshotSelectString
get { return sqlSnapshotSelectString; }
}
+ [Obsolete("Use GetSQLLazySelectString method instead")]
protected SqlString SQLLazySelectString
{
get { return sqlLazySelectString; }
}
+ protected SqlString GetSQLLazySelectString(string fetchGroup)
+ {
+ return _sqlLazySelectStringsByFetchGroup.TryGetValue(fetchGroup, out var value) ? value : null;
+ }
+
///
/// The queries that delete rows by id (and version)
///
@@ -1246,6 +1253,8 @@ public virtual bool HasRowId
get { return rowIdName != null; }
}
+ // Since 5.3
+ [Obsolete("This method has no more usage in NHibernate and will be removed in a future version.")]
protected internal virtual SqlString GenerateLazySelectString()
{
if (!entityMetamodel.HasLazyProperties)
@@ -1292,6 +1301,60 @@ protected internal virtual SqlString GenerateLazySelectString()
return RenderSelect(tableNumbers.ToArray(), columnNumbers.ToArray(), formulaNumbers.ToArray());
}
+ protected virtual IDictionary GenerateLazySelectStringsByFetchGroup()
+ {
+ var enhancementMetadata = entityMetamodel.BytecodeEnhancementMetadata;
+ if (!enhancementMetadata.EnhancedForLazyLoading || !enhancementMetadata.LazyPropertiesMetadata.HasLazyProperties)
+ {
+ return CollectionHelper.EmptyDictionary();
+ }
+
+ var result = new Dictionary();
+ var lazyPropertiesMetadata = enhancementMetadata.LazyPropertiesMetadata;
+
+ foreach (var groupName in lazyPropertiesMetadata.FetchGroupNames)
+ {
+ var tableNumbers = new HashSet();
+ var columnNumbers = new List();
+ var formulaNumbers = new List();
+
+ foreach (var lazyPropertyDescriptor in lazyPropertiesMetadata.GetFetchGroupPropertyDescriptors(groupName))
+ {
+ // all this only really needs to consider properties
+ // of this class, not its subclasses, but since we
+ // are reusing code used for sequential selects, we
+ // use the subclass closure
+ var propertyNumber = GetSubclassPropertyIndex(lazyPropertyDescriptor.Name);
+
+ var tableNumber = GetSubclassPropertyTableNumber(propertyNumber);
+ tableNumbers.Add(tableNumber);
+
+ var colNumbers = subclassPropertyColumnNumberClosure[propertyNumber];
+ columnNumbers.AddRange(colNumbers.Where(colNumber => colNumber != -1));
+
+ var formNumbers = subclassPropertyFormulaNumberClosure[propertyNumber];
+ formulaNumbers.AddRange(formNumbers.Where(colNumber => colNumber != -1));
+ }
+
+ if (columnNumbers.Count == 0 && formulaNumbers.Count == 0)
+ {
+ // only one-to-one is lazy fetched
+ continue;
+ }
+
+ result.Add(
+ groupName,
+ RenderSelect(
+ tableNumbers.ToArray(),
+ columnNumbers.ToArray(),
+ formulaNumbers.ToArray()
+ )
+ );
+ }
+
+ return result;
+ }
+
public virtual object InitializeLazyProperty(string fieldName, object entity, ISessionImplementor session)
{
object id = session.GetContextEntityIdentifier(entity);
@@ -1308,22 +1371,27 @@ public virtual object InitializeLazyProperty(string fieldName, object entity, IS
}
var uninitializedLazyProperties = InstrumentationMetadata.GetUninitializedLazyProperties(entity);
- if (HasCache && session.CacheMode.HasFlag(CacheMode.Get))
+ if (HasCache && session.CacheMode.HasFlag(CacheMode.Get) && IsLazyPropertiesCacheable)
{
CacheKey cacheKey = session.GenerateCacheKey(id, IdentifierType, EntityName);
object ce = Cache.Get(cacheKey, session.Timestamp);
if (ce != null)
{
CacheEntry cacheEntry = (CacheEntry)CacheEntryStructure.Destructure(ce, factory);
- if (!cacheEntry.AreLazyPropertiesUnfetched)
+ var initializedValue = InitializeLazyPropertiesFromCache(fieldName, entity, session, entry, cacheEntry, uninitializedLazyProperties);
+ if (initializedValue != LazyPropertyInitializer.UnfetchedProperty)
{
- //note early exit here:
- return InitializeLazyPropertiesFromCache(fieldName, entity, session, entry, cacheEntry, uninitializedLazyProperties);
+ // NOTE EARLY EXIT!!!
+ return initializedValue;
}
}
}
- return InitializeLazyPropertiesFromDatastore(fieldName, entity, session, id, entry, uninitializedLazyProperties);
+ var result = InitializeLazyPropertiesFromDatastore(fieldName, entity, session, id, entry, uninitializedLazyProperties);
+ // Update cache
+ Loader.Loader.UpdateCacheForEntity(entity, id, entry, this, session, null);
+
+ return result;
}
public void InitializeLazyProperties(
@@ -1388,6 +1456,11 @@ private object InitializeLazyPropertiesFromDatastore(
log.Debug("initializing lazy properties from datastore");
+ var lazyPropertiesMetadata = EntityMetamodel.BytecodeEnhancementMetadata.LazyPropertiesMetadata;
+ var fetchGroup = lazyPropertiesMetadata.GetFetchGroupName(fieldName);
+ var fetchGroupPropertyDescriptors = lazyPropertiesMetadata.GetFetchGroupPropertyDescriptors(fetchGroup);
+ var lazySelect = GetSQLLazySelectString(fetchGroup);
+
using (session.BeginProcess())
try
{
@@ -1396,7 +1469,6 @@ private object InitializeLazyPropertiesFromDatastore(
DbDataReader rs = null;
try
{
- SqlString lazySelect = SQLLazySelectString;
if (lazySelect != null)
{
// null sql means that the only lazy properties
@@ -1408,12 +1480,32 @@ private object InitializeLazyPropertiesFromDatastore(
rs.Read();
}
object[] snapshot = entry.LoadedState;
- for (int j = 0; j < lazyPropertyNames.Length; j++)
+ foreach (var fetchGroupPropertyDescriptor in fetchGroupPropertyDescriptors)
{
- object propValue = lazyPropertyTypes[j].NullSafeGet(rs, lazyPropertyColumnAliases[j], session, entity);
- if (InitializeLazyProperty(fieldName, entity, snapshot, j, propValue, uninitializedLazyProperties))
+ // Iterate over all fetched properties even if they are already set
+ // as their state may not be populated yet. InitializeLazyProperty
+ // method will take care of not overriding their property values and
+ // will only update the states, if they have one.
+
+ var selectedValue = fetchGroupPropertyDescriptor.Type.NullSafeGet(
+ rs,
+ lazyPropertyColumnAliases[fetchGroupPropertyDescriptor.LazyIndex],
+ session,
+ entity
+ );
+
+ var set = InitializeLazyProperty(
+ fieldName,
+ entity,
+ snapshot,
+ fetchGroupPropertyDescriptor.LazyIndex,
+ selectedValue,
+ uninitializedLazyProperties
+ );
+
+ if (set)
{
- result = propValue;
+ result = selectedValue;
}
}
}
@@ -1433,7 +1525,7 @@ private object InitializeLazyPropertiesFromDatastore(
SqlException = sqle,
Message =
"could not initialize lazy properties: " + MessageHelper.InfoString(this, id, Factory),
- Sql = SQLLazySelectString.ToString(),
+ Sql = lazySelect?.ToString(),
EntityName = EntityName,
EntityId = id
};
@@ -1452,10 +1544,22 @@ private object InitializeLazyPropertiesFromCache(
object[] snapshot = entry.LoadedState;
for (int j = 0; j < lazyPropertyNames.Length; j++)
{
- object propValue = lazyPropertyTypes[j].Assemble(disassembledValues[lazyPropertyNumbers[j]], session, entity);
- if (InitializeLazyProperty(fieldName, entity, snapshot, j, propValue, uninitializedLazyProperties))
+ var cachedValue = disassembledValues[lazyPropertyNumbers[j]];
+ if (cachedValue == LazyPropertyInitializer.UnfetchedProperty)
{
- result = propValue;
+ if (fieldName == lazyPropertyNames[j])
+ {
+ result = LazyPropertyInitializer.UnfetchedProperty;
+ }
+ // don't try to initialize the unfetched property
+ }
+ else
+ {
+ var propValue = lazyPropertyTypes[j].Assemble(cachedValue, session, entity);
+ if (InitializeLazyProperty(fieldName, entity, snapshot, j, propValue, uninitializedLazyProperties))
+ {
+ result = propValue;
+ }
}
}
@@ -3363,7 +3467,7 @@ private bool HasDirtyLazyProperties(int[] dirtyFields, object obj)
{
// When having a dirty lazy property and the entity is not yet initialized we have to use a dynamic update for
// it even if it is disabled in order to have it updated.
- return InstrumentationMetadata.GetUninitializedLazyProperties(obj).Count > 0 && dirtyFields.Any(i => PropertyLaziness[i]);
+ return InstrumentationMetadata.HasAnyUninitializedLazyProperties(obj) && dirtyFields.Any(i => PropertyLaziness[i]);
}
public object Insert(object[] fields, object obj, ISessionImplementor session)
@@ -3509,9 +3613,9 @@ protected void LogStaticSQL()
if (log.IsDebugEnabled())
{
log.Debug("Static SQL for entity: {0}", EntityName);
- if (sqlLazySelectString != null)
+ foreach (var pair in _sqlLazySelectStringsByFetchGroup)
{
- log.Debug(" Lazy select: {0}", sqlLazySelectString);
+ log.Debug(" Lazy select ({0}): {1}", pair.Key, pair.Value);
}
if (sqlVersionSelectString != null)
{
@@ -3836,7 +3940,10 @@ public virtual void PostInstantiate()
//select SQL
sqlSnapshotSelectString = GenerateSnapshotSelectString();
+#pragma warning disable 618
sqlLazySelectString = GenerateLazySelectString();
+#pragma warning restore 618
+ _sqlLazySelectStringsByFetchGroup = GenerateLazySelectStringsByFetchGroup();
sqlVersionSelectString = GenerateSelectVersionString();
if (HasInsertGeneratedProperties)
{
@@ -3985,7 +4092,7 @@ protected bool[] GetPropertiesToInsert(object[] fields)
public virtual int[] FindDirty(object[] currentState, object[] previousState, object entity, ISessionImplementor session)
{
int[] props = TypeHelper.FindDirty(
- entityMetamodel.Properties, currentState, previousState, propertyColumnUpdateable, HasUninitializedLazyProperties(entity), session);
+ entityMetamodel.Properties, currentState, previousState, propertyColumnUpdateable, session);
if (props == null)
{
@@ -4001,7 +4108,7 @@ public virtual int[] FindDirty(object[] currentState, object[] previousState, ob
public virtual int[] FindModified(object[] old, object[] current, object entity, ISessionImplementor session)
{
int[] props = TypeHelper.FindModified(
- entityMetamodel.Properties, current, old, propertyColumnUpdateable, HasUninitializedLazyProperties(entity), session);
+ entityMetamodel.Properties, current, old, propertyColumnUpdateable, session);
if (props == null)
{
return null;
@@ -4080,7 +4187,7 @@ public virtual void AfterReassociate(object entity, ISessionImplementor session)
}
else
{
- var fieldInterceptor = InstrumentationMetadata.InjectInterceptor(entity, false, session);
+ var fieldInterceptor = InstrumentationMetadata.InjectInterceptor(entity, session);
fieldInterceptor?.MarkDirty();
}
}
@@ -4245,9 +4352,16 @@ public bool HasUpdateGeneratedProperties
get { return entityMetamodel.HasUpdateGeneratedValues; }
}
+ // Since v5.3
+ [Obsolete("Use overload without lazyPropertiesAreUnfetched parameter")]
public void AfterInitialize(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
{
- EntityTuplizer.AfterInitialize(entity, lazyPropertiesAreUnfetched, session);
+ EntityTuplizer.AfterInitialize(entity, session);
+ }
+
+ public void AfterInitialize(object entity, ISessionImplementor session)
+ {
+ EntityTuplizer.AfterInitialize(entity, session);
}
public virtual bool[] PropertyUpdateability
@@ -4335,7 +4449,7 @@ public bool IsInstance(object entity)
public virtual bool HasUninitializedLazyProperties(object obj)
{
- return EntityTuplizer.HasUninitializedLazyProperties(obj);
+ return EntityMetamodel.BytecodeEnhancementMetadata.HasAnyUninitializedLazyProperties(obj);
}
public virtual void ResetIdentifier(object entity, object currentId, object currentVersion)
diff --git a/src/NHibernate/Persister/Entity/IEntityPersister.cs b/src/NHibernate/Persister/Entity/IEntityPersister.cs
index 7ba82488bb9..495e6147ddd 100644
--- a/src/NHibernate/Persister/Entity/IEntityPersister.cs
+++ b/src/NHibernate/Persister/Entity/IEntityPersister.cs
@@ -442,6 +442,8 @@ void Update(
#region stuff that is tuplizer-centric, but is passed a session
/// Called just after the entities properties have been initialized
+ // Since 5.3
+ [Obsolete("Use the extension method instead")]
void AfterInitialize(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session);
/// Called just after the entity has been reassociated with the session
@@ -623,5 +625,20 @@ public static int GetBatchSize(this IEntityPersister persister)
return 1;
}
+
+ /// Called just after the entities properties have been initialized
+ //6.0 TODO: Merge into IEntityPersister.
+ public static void AfterInitialize(this IEntityPersister persister, object entity, ISessionImplementor session)
+ {
+ if (persister is AbstractEntityPersister abstractEntityPersister)
+ {
+ abstractEntityPersister.AfterInitialize(entity, session);
+ return;
+ }
+
+#pragma warning disable 618
+ persister.AfterInitialize(entity, true, session);
+#pragma warning restore 618
+ }
}
}
diff --git a/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs b/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs
index 20fb11edb62..d6af0c552e0 100644
--- a/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs
+++ b/src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs
@@ -224,7 +224,14 @@ public object GetPropertyValue(object entity, string propertyPath)
}
}
+ // Since v5.3
+ [Obsolete("Use overload without lazyPropertiesAreUnfetched parameter")]
public virtual void AfterInitialize(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
+ {
+ AfterInitialize(entity, session);
+ }
+
+ public virtual void AfterInitialize(object entity, ISessionImplementor session)
{
}
@@ -401,7 +408,12 @@ protected virtual IProxyFactory ProxyFactory
protected virtual bool ShouldGetAllProperties(object entity)
{
- return !HasUninitializedLazyProperties(entity);
+ if (!EntityMetamodel.BytecodeEnhancementMetadata.EnhancedForLazyLoading)
+ {
+ return true;
+ }
+
+ return !EntityMetamodel.BytecodeEnhancementMetadata.HasAnyUninitializedLazyProperties(entity);
}
protected EntityMetamodel EntityMetamodel
diff --git a/src/NHibernate/Tuple/Entity/BytecodeEnhancementMetadataNonPocoImpl.cs b/src/NHibernate/Tuple/Entity/BytecodeEnhancementMetadataNonPocoImpl.cs
index cd4dc440fa3..1d3fc563d1c 100644
--- a/src/NHibernate/Tuple/Entity/BytecodeEnhancementMetadataNonPocoImpl.cs
+++ b/src/NHibernate/Tuple/Entity/BytecodeEnhancementMetadataNonPocoImpl.cs
@@ -39,7 +39,7 @@ public BytecodeEnhancementMetadataNonPocoImpl(string entityName)
public UnwrapProxyPropertiesMetadata UnwrapProxyPropertiesMetadata { get; }
///
- public IFieldInterceptor InjectInterceptor(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
+ public IFieldInterceptor InjectInterceptor(object entity, ISessionImplementor session)
{
throw new NotInstrumentedException(_errorMessage);
}
@@ -61,5 +61,11 @@ public ISet GetUninitializedLazyProperties(object[] entityState)
{
return CollectionHelper.EmptySet();
}
+
+ ///
+ public bool HasAnyUninitializedLazyProperties(object entity)
+ {
+ return false;
+ }
}
}
diff --git a/src/NHibernate/Tuple/Entity/BytecodeEnhancementMetadataPocoImpl.cs b/src/NHibernate/Tuple/Entity/BytecodeEnhancementMetadataPocoImpl.cs
index 2f1ef4007d3..988e8767b63 100644
--- a/src/NHibernate/Tuple/Entity/BytecodeEnhancementMetadataPocoImpl.cs
+++ b/src/NHibernate/Tuple/Entity/BytecodeEnhancementMetadataPocoImpl.cs
@@ -120,7 +120,7 @@ public BytecodeEnhancementMetadataPocoImpl(
public UnwrapProxyPropertiesMetadata UnwrapProxyPropertiesMetadata { get; }
///
- public IFieldInterceptor InjectInterceptor(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
+ public IFieldInterceptor InjectInterceptor(object entity, ISessionImplementor session)
{
if (!EnhancedForLazyLoading)
{
@@ -140,7 +140,7 @@ public IFieldInterceptor InjectInterceptor(object entity, bool lazyPropertiesAre
var fieldInterceptorImpl = new DefaultFieldInterceptor(
session,
- LazyPropertiesMetadata.HasLazyProperties && lazyPropertiesAreUnfetched
+ LazyPropertiesMetadata.HasLazyProperties
? new HashSet(LazyPropertiesMetadata.LazyPropertyNames)
: null,
UnwrapProxyPropertiesMetadata.UnwrapProxyPropertyNames,
@@ -210,5 +210,12 @@ public ISet GetUninitializedLazyProperties(object[] entityState)
return uninitializedProperties;
}
+
+ ///
+ public bool HasAnyUninitializedLazyProperties(object entity)
+ {
+ var interceptor = LazyPropertiesMetadata.HasLazyProperties ? ExtractInterceptor(entity) : null;
+ return interceptor?.GetUninitializedFields().Count > 0;
+ }
}
}
diff --git a/src/NHibernate/Tuple/Entity/IEntityTuplizer.cs b/src/NHibernate/Tuple/Entity/IEntityTuplizer.cs
index 397bdd595d6..75ba17dbc0c 100644
--- a/src/NHibernate/Tuple/Entity/IEntityTuplizer.cs
+++ b/src/NHibernate/Tuple/Entity/IEntityTuplizer.cs
@@ -37,6 +37,8 @@ public interface IEntityTuplizer : ITuplizer
System.Type ConcreteProxyClass { get; }
/// Is it an instrumented POCO?
+ // Since 5.3
+ [Obsolete("This property is not used and will be removed in a future version.")]
bool IsInstrumented { get; }
/// Create an entity instance initialized with the given identifier.
@@ -100,6 +102,8 @@ public interface IEntityTuplizer : ITuplizer
/// The entity being initialized.
/// Are defined lazy properties currently unfecthed
/// The session initializing this entity.
+ // Since 5.3
+ [Obsolete("Use the extension method instead")]
void AfterInitialize(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session);
/// Does this entity, for this mode, present a possibility for proxying?
@@ -117,6 +121,28 @@ public interface IEntityTuplizer : ITuplizer
/// Does the given entity instance have any currently uninitialized lazy properties?
/// The entity to be check for uninitialized lazy properties.
/// True if uninitialized lazy properties were found; false otherwise.
+ // Since 5.3
+ [Obsolete("This method has no more usage in NHibernate and will be removed in a future version.")]
bool HasUninitializedLazyProperties(object entity);
}
+
+ public static class EntityTuplizerExtensions
+ {
+ /// Called just after the entities properties have been initialized.
+ /// The entity tupilizer.
+ /// The entity being initialized.
+ /// The session initializing this entity.
+ public static void AfterInitialize(this IEntityTuplizer entityTuplizer, object entity, ISessionImplementor session)
+ {
+ if (entityTuplizer is AbstractEntityTuplizer abstractEntityTuplizer)
+ {
+ abstractEntityTuplizer.AfterInitialize(entity, session);
+ return;
+ }
+
+#pragma warning disable 618
+ entityTuplizer.AfterInitialize(entity, true, session);
+#pragma warning restore 618
+ }
+ }
}
diff --git a/src/NHibernate/Tuple/Entity/PocoEntityInstantiator.cs b/src/NHibernate/Tuple/Entity/PocoEntityInstantiator.cs
index c7aed855d50..0a4075ca643 100644
--- a/src/NHibernate/Tuple/Entity/PocoEntityInstantiator.cs
+++ b/src/NHibernate/Tuple/Entity/PocoEntityInstantiator.cs
@@ -38,7 +38,7 @@ protected override object CreateInstance()
}
var entity = _proxyFactory.GetFieldInterceptionProxy(base.CreateInstance);
- _entityMetamodel.BytecodeEnhancementMetadata.InjectInterceptor(entity, true, null);
+ _entityMetamodel.BytecodeEnhancementMetadata.InjectInterceptor(entity, null);
return entity;
}
diff --git a/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs b/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs
index 78c80515141..79285ce1ea9 100644
--- a/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs
+++ b/src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs
@@ -28,6 +28,7 @@ public class PocoEntityTuplizer : AbstractEntityTuplizer
[NonSerialized]
private IReflectionOptimizer optimizer;
private readonly IProxyValidator proxyValidator;
+ private readonly IBytecodeEnhancementMetadata _enhancementMetadata;
[NonSerialized]
private bool isBytecodeProviderImpl; // 6.0 TODO: remove
@@ -60,6 +61,7 @@ public PocoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappe
proxyInterface = mappedEntity.ProxyInterface;
islifecycleImplementor = typeof(ILifecycle).IsAssignableFrom(mappedClass);
isValidatableImplementor = typeof(IValidatable).IsAssignableFrom(mappedClass);
+ _enhancementMetadata = EntityMetamodel.BytecodeEnhancementMetadata;
SetReflectionOptimizer();
@@ -75,7 +77,7 @@ public override System.Type ConcreteProxyClass
get { return proxyInterface; }
}
- public override bool IsInstrumented => EntityMetamodel.BytecodeEnhancementMetadata.EnhancedForLazyLoading;
+ public override bool IsInstrumented => _enhancementMetadata.EnhancedForLazyLoading;
public override System.Type MappedClass
{
@@ -213,14 +215,14 @@ protected virtual IProxyFactory BuildProxyFactoryInternal(PersistentClass @class
return Cfg.Environment.BytecodeProvider.ProxyFactoryFactory.BuildProxyFactory();
}
- public override void AfterInitialize(object entity, bool lazyPropertiesAreUnfetched, ISessionImplementor session)
+ public override void AfterInitialize(object entity, ISessionImplementor session)
{
if (IsInstrumented)
{
- var interceptor = EntityMetamodel.BytecodeEnhancementMetadata.ExtractInterceptor(entity);
+ var interceptor = _enhancementMetadata.ExtractInterceptor(entity);
if (interceptor == null)
{
- interceptor = EntityMetamodel.BytecodeEnhancementMetadata.InjectInterceptor(entity, lazyPropertiesAreUnfetched, session);
+ interceptor = _enhancementMetadata.InjectInterceptor(entity, session);
}
else
{
@@ -274,8 +276,7 @@ public override bool HasUninitializedLazyProperties(object entity)
{
if (EntityMetamodel.HasLazyProperties)
{
- var callback = EntityMetamodel.BytecodeEnhancementMetadata.ExtractInterceptor(entity);
- return callback != null && !callback.IsInitialized;
+ return _enhancementMetadata.HasAnyUninitializedLazyProperties(entity);
}
else
{
@@ -290,7 +291,7 @@ internal override ISet GetUninitializedLazyProperties(object entity)
return CollectionHelper.EmptySet();
}
- return EntityMetamodel.BytecodeEnhancementMetadata.GetUninitializedLazyProperties(entity);
+ return _enhancementMetadata.GetUninitializedLazyProperties(entity);
}
public override bool IsLifecycleImplementor
diff --git a/src/NHibernate/Tuple/StandardProperty.cs b/src/NHibernate/Tuple/StandardProperty.cs
index 4349e815b99..59bb5b10782 100644
--- a/src/NHibernate/Tuple/StandardProperty.cs
+++ b/src/NHibernate/Tuple/StandardProperty.cs
@@ -96,6 +96,8 @@ public bool IsNullable
get { return nullable; }
}
+ // Since 5.3
+ [Obsolete("This method has no more usage in NHibernate and will be removed in a future version.")]
public bool IsDirtyCheckable(bool hasUninitializedProperties)
{
return IsDirtyCheckable() && (!hasUninitializedProperties || !IsLazy);
@@ -121,4 +123,4 @@ public FetchMode? FetchMode
get { return fetchMode; }
}
}
-}
\ No newline at end of file
+}
diff --git a/src/NHibernate/Type/TypeHelper.cs b/src/NHibernate/Type/TypeHelper.cs
index a447ef7f64e..56a676cc87a 100644
--- a/src/NHibernate/Type/TypeHelper.cs
+++ b/src/NHibernate/Type/TypeHelper.cs
@@ -265,12 +265,34 @@ public static object[] ReplaceAssociations(object[] original, object[] target, I
/// Does the entity currently hold any uninitialized property values?
/// The session from which the dirty check request originated.
/// Array containing indices of the dirty properties, or null if no properties considered dirty.
+ // Since 5.3
+ [Obsolete("Use overload without anyUninitializedProperties parameter instead")]
public static int[] FindDirty(StandardProperty[] properties,
object[] currentState,
object[] previousState,
bool[][] includeColumns,
bool anyUninitializedProperties,
ISessionImplementor session)
+ {
+ return FindDirty(properties, currentState, previousState, includeColumns, session);
+ }
+
+ ///
+ /// Determine if any of the given field values are dirty, returning an array containing
+ /// indices of the dirty fields.
+ /// If it is determined that no fields are dirty, null is returned.
+ ///
+ /// The property definitions
+ /// The current state of the entity
+ /// The baseline state of the entity
+ /// Columns to be included in the dirty checking, per property
+ /// The session from which the dirty check request originated.
+ /// Array containing indices of the dirty properties, or null if no properties considered dirty.
+ public static int[] FindDirty(StandardProperty[] properties,
+ object[] currentState,
+ object[] previousState,
+ bool[][] includeColumns,
+ ISessionImplementor session)
{
int[] results = null;
int count = 0;
@@ -278,7 +300,7 @@ public static int[] FindDirty(StandardProperty[] properties,
for (int i = 0; i < span; i++)
{
- var dirty = Dirty(properties, currentState, previousState, includeColumns, anyUninitializedProperties, session, i);
+ var dirty = Dirty(properties, currentState, previousState, includeColumns, session, i);
if (dirty)
{
if (results == null)
@@ -300,13 +322,13 @@ public static int[] FindDirty(StandardProperty[] properties,
}
}
- private static bool Dirty(StandardProperty[] properties, object[] currentState, object[] previousState, bool[][] includeColumns, bool anyUninitializedProperties, ISessionImplementor session, int i)
+ private static bool Dirty(StandardProperty[] properties, object[] currentState, object[] previousState, bool[][] includeColumns, ISessionImplementor session, int i)
{
if (Equals(LazyPropertyInitializer.UnfetchedProperty, currentState[i]))
return false;
if (Equals(LazyPropertyInitializer.UnfetchedProperty, previousState[i]))
return true;
- return properties[i].IsDirtyCheckable(anyUninitializedProperties) &&
+ return properties[i].IsDirtyCheckable() &&
properties[i].Type.IsDirty(previousState[i], currentState[i], includeColumns[i], session);
}
@@ -322,12 +344,34 @@ private static bool Dirty(StandardProperty[] properties, object[] currentState,
/// Does the entity currently hold any uninitialized property values?
/// The session from which the dirty check request originated.
/// Array containing indices of the modified properties, or null if no properties considered modified.
+ // Since 5.3
+ [Obsolete("Use the overload without anyUninitializedProperties parameter.")]
public static int[] FindModified(StandardProperty[] properties,
object[] currentState,
object[] previousState,
bool[][] includeColumns,
bool anyUninitializedProperties,
ISessionImplementor session)
+ {
+ return FindModified(properties, currentState, previousState, includeColumns, session);
+ }
+
+ ///
+ /// Determine if any of the given field values are modified, returning an array containing
+ /// indices of the modified fields.
+ /// If it is determined that no fields are dirty, null is returned.
+ ///
+ /// The property definitions
+ /// The current state of the entity
+ /// The baseline state of the entity
+ /// Columns to be included in the mod checking, per property
+ /// The session from which the dirty check request originated.
+ /// Array containing indices of the modified properties, or null if no properties considered modified.
+ public static int[] FindModified(StandardProperty[] properties,
+ object[] currentState,
+ object[] previousState,
+ bool[][] includeColumns,
+ ISessionImplementor session)
{
int[] results = null;
int count = 0;
@@ -337,7 +381,7 @@ public static int[] FindModified(StandardProperty[] properties,
{
bool dirty =
!Equals(LazyPropertyInitializer.UnfetchedProperty, currentState[i]) &&
- properties[i].IsDirtyCheckable(anyUninitializedProperties)
+ properties[i].IsDirtyCheckable()
&& properties[i].Type.IsModified(previousState[i], currentState[i], includeColumns[i], session);
if (dirty)
diff --git a/src/NHibernate/nhibernate-mapping.xsd b/src/NHibernate/nhibernate-mapping.xsd
index 1b4c13b99a7..ed1ce498221 100644
--- a/src/NHibernate/nhibernate-mapping.xsd
+++ b/src/NHibernate/nhibernate-mapping.xsd
@@ -280,6 +280,7 @@
+
@@ -1168,6 +1169,7 @@
+