Skip to content

Commit 86e05bf

Browse files
ValResnickhazzik
andauthored
Fix ISession.Refresh not updating initialized lazy properties (#3697)
Co-authored-by: Alex Zaytsev <[email protected]>
1 parent 66300c1 commit 86e05bf

File tree

6 files changed

+109
-5
lines changed

6 files changed

+109
-5
lines changed

src/NHibernate.Test/Async/FetchLazyProperties/FetchLazyPropertiesFixture.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,41 @@ void AssertPersons(List<Person> results, bool fetched)
10861086
}
10871087
}
10881088
}
1089+
1090+
[Test]
1091+
public async Task TestRefreshRemovesLazyLoadedPropertiesAsync()
1092+
{
1093+
using (var outerSession = OpenSession())
1094+
{
1095+
const string query = "from Person fetch Image where Id = 1";
1096+
const string namePostFix = "_MODIFIED";
1097+
const int imageLength = 1985;
1098+
1099+
Person outerPerson = await (outerSession.CreateQuery(query).UniqueResultAsync<Person>());
1100+
1101+
Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.False); // Normal property
1102+
Assert.That(outerPerson.Image.Length, Is.EqualTo(1)); // Lazy Property
1103+
1104+
// Changing the properties of the person in a different sessions
1105+
using (var innerSession = OpenSession())
1106+
{
1107+
var transaction = innerSession.BeginTransaction();
1108+
1109+
Person innerPerson = await (innerSession.CreateQuery(query).UniqueResultAsync<Person>());
1110+
innerPerson.Image = new byte[imageLength];
1111+
innerPerson.Name += namePostFix;
1112+
await (innerSession.UpdateAsync(innerPerson));
1113+
1114+
await (transaction.CommitAsync());
1115+
}
1116+
1117+
// Refreshing the person in the outer session
1118+
await (outerSession.RefreshAsync(outerPerson));
1119+
1120+
Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.True); // Value has changed
1121+
Assert.That(outerPerson.Image.Length, Is.EqualTo(imageLength)); // This is still the old value
1122+
}
1123+
}
10891124

10901125
private static Person GeneratePerson(int i, Person bestFriend)
10911126
{

src/NHibernate.Test/FetchLazyProperties/FetchLazyPropertiesFixture.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,41 @@ void AssertPersons(List<Person> results, bool fetched)
10751075
}
10761076
}
10771077
}
1078+
1079+
[Test]
1080+
public void TestRefreshRemovesLazyLoadedProperties()
1081+
{
1082+
using (var outerSession = OpenSession())
1083+
{
1084+
const string query = "from Person fetch Image where Id = 1";
1085+
const string namePostFix = "_MODIFIED";
1086+
const int imageLength = 1985;
1087+
1088+
Person outerPerson = outerSession.CreateQuery(query).UniqueResult<Person>();
1089+
1090+
Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.False); // Normal property
1091+
Assert.That(outerPerson.Image.Length, Is.EqualTo(1)); // Lazy Property
1092+
1093+
// Changing the properties of the person in a different sessions
1094+
using (var innerSession = OpenSession())
1095+
{
1096+
var transaction = innerSession.BeginTransaction();
1097+
1098+
Person innerPerson = innerSession.CreateQuery(query).UniqueResult<Person>();
1099+
innerPerson.Image = new byte[imageLength];
1100+
innerPerson.Name += namePostFix;
1101+
innerSession.Update(innerPerson);
1102+
1103+
transaction.Commit();
1104+
}
1105+
1106+
// Refreshing the person in the outer session
1107+
outerSession.Refresh(outerPerson);
1108+
1109+
Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.True); // Value has changed
1110+
Assert.That(outerPerson.Image.Length, Is.EqualTo(imageLength)); // This is still the old value
1111+
}
1112+
}
10781113

10791114
private static Person GeneratePerson(int i, Person bestFriend)
10801115
{

src/NHibernate/Async/Event/Default/DefaultRefreshEventListener.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using NHibernate.Cache;
1515
using NHibernate.Engine;
1616
using NHibernate.Impl;
17+
using NHibernate.Intercept;
1718
using NHibernate.Persister.Entity;
1819
using NHibernate.Type;
1920
using NHibernate.Util;
@@ -115,7 +116,9 @@ public virtual async Task OnRefreshAsync(RefreshEvent @event, IDictionary refres
115116
}
116117

117118
await (EvictCachedCollectionsAsync(persister, id, source.Factory, cancellationToken)).ConfigureAwait(false);
118-
119+
120+
RefreshLazyProperties(persister, obj);
121+
119122
// NH Different behavior : NH-1601
120123
// At this point the entity need the real refresh, all elementes of collections are Refreshed,
121124
// the collection state was evicted, but the PersistentCollection (in the entity state)

src/NHibernate/Event/Default/DefaultRefreshEventListener.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using NHibernate.Cache;
55
using NHibernate.Engine;
66
using NHibernate.Impl;
7+
using NHibernate.Intercept;
78
using NHibernate.Persister.Entity;
89
using NHibernate.Type;
910
using NHibernate.Util;
@@ -97,7 +98,9 @@ public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready)
9798
}
9899

99100
EvictCachedCollections(persister, id, source.Factory);
100-
101+
102+
RefreshLazyProperties(persister, obj);
103+
101104
// NH Different behavior : NH-1601
102105
// At this point the entity need the real refresh, all elementes of collections are Refreshed,
103106
// the collection state was evicted, but the PersistentCollection (in the entity state)
@@ -142,5 +145,17 @@ private void EvictCachedCollections(IType[] types, object id, ISessionFactoryImp
142145
}
143146
}
144147
}
148+
149+
private static void RefreshLazyProperties(IEntityPersister persister, object obj)
150+
{
151+
if (obj == null)
152+
return;
153+
154+
if (persister.IsInstrumented)
155+
{
156+
// The list of initialized lazy fields have to be cleared in order to refresh them from the database.
157+
persister.EntityMetamodel.BytecodeEnhancementMetadata.ExtractInterceptor(obj)?.ClearInitializedLazyFields();
158+
}
159+
}
145160
}
146161
}

src/NHibernate/Intercept/AbstractFieldInterceptor.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using Iesi.Collections.Generic;
44
using NHibernate.Engine;
5-
using NHibernate.Persister.Entity;
65
using NHibernate.Proxy;
76
using NHibernate.Util;
87

@@ -33,7 +32,7 @@ protected internal AbstractFieldInterceptor(ISessionImplementor session, ISet<st
3332
this.unwrapProxyFieldNames = unwrapProxyFieldNames ?? new HashSet<string>();
3433
this.entityName = entityName;
3534
this.mappedClass = mappedClass;
36-
this.uninitializedFieldsReadOnly = uninitializedFields != null ? new ReadOnlySet<string>(uninitializedFields) : null;
35+
this.uninitializedFieldsReadOnly = uninitializedFields != null ? new ReadOnlySet<string>(new HashSet<string>(uninitializedFields)) : null;
3736
}
3837

3938
#region IFieldInterceptor Members
@@ -207,7 +206,15 @@ private object InitializeField(string fieldName, object target)
207206

208207
public ISet<string> GetUninitializedFields()
209208
{
210-
return uninitializedFieldsReadOnly ?? CollectionHelper.EmptySet<string>();
209+
return uninitializedFields ?? CollectionHelper.EmptySet<string>();
210+
}
211+
212+
public void ClearInitializedLazyFields()
213+
{
214+
if (uninitializedFieldsReadOnly != null)
215+
{
216+
uninitializedFields.UnionWith(uninitializedFieldsReadOnly);
217+
}
211218
}
212219
}
213220
}

src/NHibernate/Intercept/IFieldInterceptor.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,14 @@ public static object Intercept(this IFieldInterceptor interceptor, object target
7474
return interceptor.Intercept(target, fieldName, value);
7575
#pragma warning restore 618
7676
}
77+
78+
// 6.0 TODO: merge into IFieldInterceptor
79+
public static void ClearInitializedLazyFields(this IFieldInterceptor interceptor)
80+
{
81+
if (interceptor is AbstractFieldInterceptor fieldInterceptor)
82+
{
83+
fieldInterceptor.ClearInitializedLazyFields();
84+
}
85+
}
7786
}
7887
}

0 commit comments

Comments
 (0)