Skip to content

Commit 9a8ac77

Browse files
bahusoidsashakboo
andauthoredJul 1, 2023
Fix lazy property handling with field accessors (#3345)
Fixes #3333 Co-authored-by: sashakboo <31510839+sashakboo@users.noreply.github.com>
1 parent 8067451 commit 9a8ac77

File tree

5 files changed

+130
-1
lines changed

5 files changed

+130
-1
lines changed
 

‎src/NHibernate.Test/Async/LazyProperty/LazyPropertyFixture.cs

+55-1
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
using System.Linq;
1414
using NHibernate.Cfg;
1515
using NHibernate.Intercept;
16+
using NHibernate.Linq;
1617
using NHibernate.Tuple.Entity;
1718
using NUnit.Framework;
1819
using NUnit.Framework.Constraints;
19-
using NHibernate.Linq;
2020

2121
namespace NHibernate.Test.LazyProperty
2222
{
@@ -69,6 +69,7 @@ protected override void OnSetUp()
6969
Id = 1,
7070
ALotOfText = "a lot of text ...",
7171
Image = new byte[10],
72+
NoSetterImage = new byte[10],
7273
FieldInterceptor = "Why not that name?"
7374
});
7475
tx.Commit();
@@ -393,5 +394,58 @@ public async Task CanMergeTransientWithLazyPropertyInCollectionAsync()
393394
Assert.That(book.Words.First().Content, Is.EqualTo(new byte[1] { 0 }));
394395
}
395396
}
397+
398+
[Test(Description = "GH-3333")]
399+
public async Task GetLazyPropertyWithNoSetterAccessor_PropertyShouldBeInitializedAsync()
400+
{
401+
using (ISession s = OpenSession())
402+
{
403+
var book = await (s.GetAsync<Book>(1));
404+
var image = book.NoSetterImage;
405+
// Fails. Property remains uninitialized after it has been accessed.
406+
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "NoSetterImage"), Is.True);
407+
}
408+
}
409+
410+
[Test(Description = "GH-3333")]
411+
public async Task GetLazyPropertyWithNoSetterAccessorTwice_ResultsAreSameObjectAsync()
412+
{
413+
using (ISession s = OpenSession())
414+
{
415+
var book = await (s.GetAsync<Book>(1));
416+
var image = book.NoSetterImage;
417+
var sameImage = book.NoSetterImage;
418+
// Fails. Each call to a property getter returns a new object.
419+
Assert.That(ReferenceEquals(image, sameImage), Is.True);
420+
}
421+
}
422+
423+
[Test]
424+
public async Task CanSetValueForLazyPropertyNoSetterAsync()
425+
{
426+
Book book;
427+
using (ISession s = OpenSession())
428+
{
429+
book = await (s.GetAsync<Book>(1));
430+
book.NoSetterImage = new byte[]{10};
431+
}
432+
433+
Assert.That(NHibernateUtil.IsPropertyInitialized(book, nameof(book.NoSetterImage)), Is.True);
434+
CollectionAssert.AreEqual(book.NoSetterImage, new byte[] { 10 });
435+
}
436+
437+
[Test]
438+
public async Task CanFetchLazyPropertyNoSetterAsync()
439+
{
440+
using (ISession s = OpenSession())
441+
{
442+
var book = await (s
443+
.Query<Book>()
444+
.Fetch(x => x.NoSetterImage)
445+
.FirstAsync(x => x.Id == 1));
446+
447+
Assert.That(NHibernateUtil.IsPropertyInitialized(book, nameof(book.NoSetterImage)), Is.True);
448+
}
449+
}
396450
}
397451
}

‎src/NHibernate.Test/LazyProperty/Book.cs

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ public virtual string ALotOfText
1717

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

20+
private byte[] _NoSetterImage;
21+
22+
public virtual byte[] NoSetterImage
23+
{
24+
get { return _NoSetterImage; }
25+
set { _NoSetterImage = value; }
26+
}
27+
2028
public virtual string FieldInterceptor { get; set; }
2129

2230
public virtual IList<Word> Words { get; set; }

‎src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs

+55
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using NHibernate.Cfg;
55
using NHibernate.Intercept;
6+
using NHibernate.Linq;
67
using NHibernate.Tuple.Entity;
78
using NUnit.Framework;
89
using NUnit.Framework.Constraints;
@@ -57,6 +58,7 @@ protected override void OnSetUp()
5758
Id = 1,
5859
ALotOfText = "a lot of text ...",
5960
Image = new byte[10],
61+
NoSetterImage = new byte[10],
6062
FieldInterceptor = "Why not that name?"
6163
});
6264
tx.Commit();
@@ -387,5 +389,58 @@ public void CanMergeTransientWithLazyPropertyInCollection()
387389
Assert.That(book.Words.First().Content, Is.EqualTo(new byte[1] { 0 }));
388390
}
389391
}
392+
393+
[Test(Description = "GH-3333")]
394+
public void GetLazyPropertyWithNoSetterAccessor_PropertyShouldBeInitialized()
395+
{
396+
using (ISession s = OpenSession())
397+
{
398+
var book = s.Get<Book>(1);
399+
var image = book.NoSetterImage;
400+
// Fails. Property remains uninitialized after it has been accessed.
401+
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "NoSetterImage"), Is.True);
402+
}
403+
}
404+
405+
[Test(Description = "GH-3333")]
406+
public void GetLazyPropertyWithNoSetterAccessorTwice_ResultsAreSameObject()
407+
{
408+
using (ISession s = OpenSession())
409+
{
410+
var book = s.Get<Book>(1);
411+
var image = book.NoSetterImage;
412+
var sameImage = book.NoSetterImage;
413+
// Fails. Each call to a property getter returns a new object.
414+
Assert.That(ReferenceEquals(image, sameImage), Is.True);
415+
}
416+
}
417+
418+
[Test]
419+
public void CanSetValueForLazyPropertyNoSetter()
420+
{
421+
Book book;
422+
using (ISession s = OpenSession())
423+
{
424+
book = s.Get<Book>(1);
425+
book.NoSetterImage = new byte[]{10};
426+
}
427+
428+
Assert.That(NHibernateUtil.IsPropertyInitialized(book, nameof(book.NoSetterImage)), Is.True);
429+
CollectionAssert.AreEqual(book.NoSetterImage, new byte[] { 10 });
430+
}
431+
432+
[Test]
433+
public void CanFetchLazyPropertyNoSetter()
434+
{
435+
using (ISession s = OpenSession())
436+
{
437+
var book = s
438+
.Query<Book>()
439+
.Fetch(x => x.NoSetterImage)
440+
.First(x => x.Id == 1);
441+
442+
Assert.That(NHibernateUtil.IsPropertyInitialized(book, nameof(book.NoSetterImage)), Is.True);
443+
}
444+
}
390445
}
391446
}

‎src/NHibernate.Test/LazyProperty/Mappings.hbm.xml

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<property name="Name" />
1212
<property name="ALotOfText" lazy="true" />
1313
<property name="Image" lazy="true" />
14+
<property name="NoSetterImage" access="nosetter.pascalcase-underscore" lazy="true" />
1415
<property name="FieldInterceptor" />
1516
<bag name="Words" inverse="true" generic="true" cascade="all-delete-orphan" lazy="true" >
1617
<key column="ParentId" />

‎src/NHibernate/Tuple/Entity/PocoEntityTuplizer.cs

+11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using NHibernate.Util;
1313
using System.Runtime.Serialization;
1414
using NHibernate.Bytecode.Lightweight;
15+
using NHibernate.Intercept;
1516

1617
namespace NHibernate.Tuple.Entity
1718
{
@@ -306,6 +307,16 @@ public override bool IsLifecycleImplementor
306307

307308
public override void SetPropertyValue(object entity, int i, object value)
308309
{
310+
// If there is no property setter we need to manually intercept value for proper lazy property handling.
311+
if (IsInstrumented && setters[i].PropertyName == null)
312+
{
313+
IFieldInterceptor interceptor = _enhancementMetadata.ExtractInterceptor(entity);
314+
if (interceptor != null)
315+
{
316+
value = interceptor.Intercept(entity, EntityMetamodel.PropertyNames[i], value, true);
317+
}
318+
}
319+
309320
if (isBytecodeProviderImpl && optimizer?.AccessOptimizer != null)
310321
{
311322
optimizer.AccessOptimizer.SetPropertyValue(entity, i, value);

0 commit comments

Comments
 (0)
Please sign in to comment.