Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions releasenotes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,23 @@ Release notes - NHibernate - Version 5.4.0
* #2242 Test case for NH-3972 - SQL error when selecting a column of a subclass when sibling classes have a column of the same name


Build 5.3.18
=============================

Release notes - NHibernate - Version 5.3.18

3 issues were resolved in this release.

** Bug

* #3333 Lazy property with nosetter accessor remains uninitialized
* #3330 Linq with FetchLazyProperties() resets lazy property changes

** Task

* #3346 Release 5.3.18


Build 5.3.17
=============================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,45 @@ public async Task TestLinqFetchAllPropertiesAsync()
AssertFetchAllProperties(person);
}

[TestCase(true)]
[TestCase(false)]
public async Task TestLinqFetchAllProperties_WhenLazyPropertyChangedAsync(bool initLazyPropertyFetchGroup)
{
Person person;
using (var s = OpenSession())
{
person = await (s.GetAsync<Person>(1));
if (initLazyPropertyFetchGroup)
CollectionAssert.AreEqual(new byte[] { 0 }, person.Image);

person.Image = new byte[] { 1, 2, 3 };

var allPersons = await (s.Query<Person>().FetchLazyProperties().ToListAsync());
// After execute FetchLazyProperties(), I expected to see that the person.Image would be { 1, 2, 3 }.
// Because I changed this person.Image manually, I didn't want to lose those changes.
// But test failed. Оld value returned { 0 }.
CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, person.Image);
}
}

[TestCase(true)]
[TestCase(false)]
public async Task TestLinqFetchProperty_WhenLazyPropertyChangedAsync(bool initLazyPropertyFetchGroup)
{
Person person;
using (var s = OpenSession())
{
person = await (s.GetAsync<Person>(1));
if (initLazyPropertyFetchGroup)
CollectionAssert.AreEqual(new byte[] { 0 }, person.Image);

person.Image = new byte[] { 1, 2, 3 };

var allPersons = await (s.Query<Person>().Fetch(x => x.Image).ToListAsync());
CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, person.Image);
}
}

private static void AssertFetchAllProperties(Person person)
{
Assert.That(person, Is.Not.Null);
Expand Down
56 changes: 55 additions & 1 deletion src/NHibernate.Test/Async/LazyProperty/LazyPropertyFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
using System.Linq;
using NHibernate.Cfg;
using NHibernate.Intercept;
using NHibernate.Linq;
using NHibernate.Tuple.Entity;
using NUnit.Framework;
using NUnit.Framework.Constraints;
using NHibernate.Linq;

namespace NHibernate.Test.LazyProperty
{
Expand Down Expand Up @@ -67,6 +67,7 @@ protected override void OnSetUp()
Id = 1,
ALotOfText = "a lot of text ...",
Image = new byte[10],
NoSetterImage = new byte[10],
FieldInterceptor = "Why not that name?"
});
tx.Commit();
Expand Down Expand Up @@ -391,5 +392,58 @@ public async Task CanMergeTransientWithLazyPropertyInCollectionAsync()
Assert.That(book.Words.First().Content, Is.EqualTo(new byte[1] { 0 }));
}
}

[Test(Description = "GH-3333")]
public async Task GetLazyPropertyWithNoSetterAccessor_PropertyShouldBeInitializedAsync()
{
using (ISession s = OpenSession())
{
var book = await (s.GetAsync<Book>(1));
var image = book.NoSetterImage;
// Fails. Property remains uninitialized after it has been accessed.
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "NoSetterImage"), Is.True);
}
}

[Test(Description = "GH-3333")]
public async Task GetLazyPropertyWithNoSetterAccessorTwice_ResultsAreSameObjectAsync()
{
using (ISession s = OpenSession())
{
var book = await (s.GetAsync<Book>(1));
var image = book.NoSetterImage;
var sameImage = book.NoSetterImage;
// Fails. Each call to a property getter returns a new object.
Assert.That(ReferenceEquals(image, sameImage), Is.True);
}
}

[Test]
public async Task CanSetValueForLazyPropertyNoSetterAsync()
{
Book book;
using (ISession s = OpenSession())
{
book = await (s.GetAsync<Book>(1));
book.NoSetterImage = new byte[]{10};
}

Assert.That(NHibernateUtil.IsPropertyInitialized(book, nameof(book.NoSetterImage)), Is.True);
CollectionAssert.AreEqual(book.NoSetterImage, new byte[] { 10 });
}

[Test]
public async Task CanFetchLazyPropertyNoSetterAsync()
{
using (ISession s = OpenSession())
{
var book = await (s
.Query<Book>()
.Fetch(x => x.NoSetterImage)
.FirstAsync(x => x.Id == 1));

Assert.That(NHibernateUtil.IsPropertyInitialized(book, nameof(book.NoSetterImage)), Is.True);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,45 @@ public void TestLinqFetchAllProperties()
AssertFetchAllProperties(person);
}

[TestCase(true)]
[TestCase(false)]
public void TestLinqFetchAllProperties_WhenLazyPropertyChanged(bool initLazyPropertyFetchGroup)
{
Person person;
using (var s = OpenSession())
{
person = s.Get<Person>(1);
if (initLazyPropertyFetchGroup)
CollectionAssert.AreEqual(new byte[] { 0 }, person.Image);

person.Image = new byte[] { 1, 2, 3 };

var allPersons = s.Query<Person>().FetchLazyProperties().ToList();
// After execute FetchLazyProperties(), I expected to see that the person.Image would be { 1, 2, 3 }.
// Because I changed this person.Image manually, I didn't want to lose those changes.
// But test failed. Оld value returned { 0 }.
CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, person.Image);
}
}

[TestCase(true)]
[TestCase(false)]
public void TestLinqFetchProperty_WhenLazyPropertyChanged(bool initLazyPropertyFetchGroup)
{
Person person;
using (var s = OpenSession())
{
person = s.Get<Person>(1);
if (initLazyPropertyFetchGroup)
CollectionAssert.AreEqual(new byte[] { 0 }, person.Image);

person.Image = new byte[] { 1, 2, 3 };

var allPersons = s.Query<Person>().Fetch(x => x.Image).ToList();
CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, person.Image);
}
}

private static void AssertFetchAllProperties(Person person)
{
Assert.That(person, Is.Not.Null);
Expand Down
8 changes: 8 additions & 0 deletions src/NHibernate.Test/LazyProperty/Book.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ public virtual string ALotOfText

public virtual byte[] Image { get; set; }

private byte[] _NoSetterImage;

public virtual byte[] NoSetterImage
{
get { return _NoSetterImage; }
set { _NoSetterImage = value; }
}

public virtual string FieldInterceptor { get; set; }

public virtual IList<Word> Words { get; set; }
Expand Down
55 changes: 55 additions & 0 deletions src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using NHibernate.Cfg;
using NHibernate.Intercept;
using NHibernate.Linq;
using NHibernate.Tuple.Entity;
using NUnit.Framework;
using NUnit.Framework.Constraints;
Expand Down Expand Up @@ -55,6 +56,7 @@ protected override void OnSetUp()
Id = 1,
ALotOfText = "a lot of text ...",
Image = new byte[10],
NoSetterImage = new byte[10],
FieldInterceptor = "Why not that name?"
});
tx.Commit();
Expand Down Expand Up @@ -385,5 +387,58 @@ public void CanMergeTransientWithLazyPropertyInCollection()
Assert.That(book.Words.First().Content, Is.EqualTo(new byte[1] { 0 }));
}
}

[Test(Description = "GH-3333")]
public void GetLazyPropertyWithNoSetterAccessor_PropertyShouldBeInitialized()
{
using (ISession s = OpenSession())
{
var book = s.Get<Book>(1);
var image = book.NoSetterImage;
// Fails. Property remains uninitialized after it has been accessed.
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "NoSetterImage"), Is.True);
}
}

[Test(Description = "GH-3333")]
public void GetLazyPropertyWithNoSetterAccessorTwice_ResultsAreSameObject()
{
using (ISession s = OpenSession())
{
var book = s.Get<Book>(1);
var image = book.NoSetterImage;
var sameImage = book.NoSetterImage;
// Fails. Each call to a property getter returns a new object.
Assert.That(ReferenceEquals(image, sameImage), Is.True);
}
}

[Test]
public void CanSetValueForLazyPropertyNoSetter()
{
Book book;
using (ISession s = OpenSession())
{
book = s.Get<Book>(1);
book.NoSetterImage = new byte[]{10};
}

Assert.That(NHibernateUtil.IsPropertyInitialized(book, nameof(book.NoSetterImage)), Is.True);
CollectionAssert.AreEqual(book.NoSetterImage, new byte[] { 10 });
}

[Test]
public void CanFetchLazyPropertyNoSetter()
{
using (ISession s = OpenSession())
{
var book = s
.Query<Book>()
.Fetch(x => x.NoSetterImage)
.First(x => x.Id == 1);

Assert.That(NHibernateUtil.IsPropertyInitialized(book, nameof(book.NoSetterImage)), Is.True);
}
}
}
}
1 change: 1 addition & 0 deletions src/NHibernate.Test/LazyProperty/Mappings.hbm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<property name="Name" />
<property name="ALotOfText" lazy="true" />
<property name="Image" lazy="true" />
<property name="NoSetterImage" access="nosetter.pascalcase-underscore" lazy="true" />
<property name="FieldInterceptor" />
<bag name="Words" inverse="true" generic="true" cascade="all-delete-orphan" lazy="true" >
<key column="ParentId" />
Expand Down
11 changes: 8 additions & 3 deletions src/NHibernate/Async/Loader/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -798,8 +798,13 @@ private async Task UpdateLazyPropertiesFromResultSetAsync(DbDataReader rs, int i
? persister.EntityMetamodel.BytecodeEnhancementMetadata.GetUninitializedLazyProperties(entry.LoadedState)
: persister.EntityMetamodel.BytecodeEnhancementMetadata.GetUninitializedLazyProperties(obj);

var updateLazyProperties = fetchLazyProperties?.Intersect(uninitializedProperties).ToArray();
if (updateLazyProperties?.Length == 0)
if (uninitializedProperties.Count == 0)
return;

var updateLazyProperties = fetchAllProperties
? uninitializedProperties.ToArray()
: fetchLazyProperties.Intersect(uninitializedProperties).ToArray();
if (updateLazyProperties.Length == 0)
{
return; // No new lazy properites were loaded
}
Expand All @@ -815,7 +820,7 @@ private async Task UpdateLazyPropertiesFromResultSetAsync(DbDataReader rs, int i
? EntityAliases[i].SuffixedPropertyAliases
: GetSubclassEntityAliases(i, persister);

if (!await (persister.InitializeLazyPropertiesAsync(rs, id, obj, cols, updateLazyProperties, fetchAllProperties, session, cancellationToken)).ConfigureAwait(false))
if (!await (persister.InitializeLazyPropertiesAsync(rs, id, obj, cols, updateLazyProperties, false, session, cancellationToken)).ConfigureAwait(false))
{
return;
}
Expand Down
11 changes: 8 additions & 3 deletions src/NHibernate/Loader/Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1204,8 +1204,13 @@ private void UpdateLazyPropertiesFromResultSet(DbDataReader rs, int i, object ob
? persister.EntityMetamodel.BytecodeEnhancementMetadata.GetUninitializedLazyProperties(entry.LoadedState)
: persister.EntityMetamodel.BytecodeEnhancementMetadata.GetUninitializedLazyProperties(obj);

var updateLazyProperties = fetchLazyProperties?.Intersect(uninitializedProperties).ToArray();
if (updateLazyProperties?.Length == 0)
if (uninitializedProperties.Count == 0)
return;

var updateLazyProperties = fetchAllProperties
? uninitializedProperties.ToArray()
: fetchLazyProperties.Intersect(uninitializedProperties).ToArray();
if (updateLazyProperties.Length == 0)
{
return; // No new lazy properites were loaded
}
Expand All @@ -1221,7 +1226,7 @@ private void UpdateLazyPropertiesFromResultSet(DbDataReader rs, int i, object ob
? EntityAliases[i].SuffixedPropertyAliases
: GetSubclassEntityAliases(i, persister);

if (!persister.InitializeLazyProperties(rs, id, obj, cols, updateLazyProperties, fetchAllProperties, session))
if (!persister.InitializeLazyProperties(rs, id, obj, cols, updateLazyProperties, false, session))
{
return;
}
Expand Down
11 changes: 11 additions & 0 deletions src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using NHibernate.Util;
using System.Runtime.Serialization;
using NHibernate.Bytecode.Lightweight;
using NHibernate.Intercept;

namespace NHibernate.Tuple.Entity
{
Expand Down Expand Up @@ -306,6 +307,16 @@ public override bool IsLifecycleImplementor

public override void SetPropertyValue(object entity, int i, object value)
{
// If there is no property setter we need to manually intercept value for proper lazy property handling.
if (IsInstrumented && setters[i].PropertyName == null)
{
IFieldInterceptor interceptor = _enhancementMetadata.ExtractInterceptor(entity);
if (interceptor != null)
{
value = interceptor.Intercept(entity, EntityMetamodel.PropertyNames[i], value, true);
}
}

if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null)
{
optimizer.AccessOptimizer.SetPropertyValue(entity, i, value);
Expand Down