Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,41 @@ void AssertPersons(List<Person> results, bool fetched)
}
}
}

[Test]
public async Task TestRefreshRemovesLazyLoadedPropertiesAsync()
{
using (var outerSession = OpenSession())
{
const string query = "from Person fetch Image where Id = 1";
const string namePostFix = "_MODIFIED";
const int imageLength = 4711;

Person outerPerson = await (outerSession.CreateQuery(query).UniqueResultAsync<Person>());

Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.False); // Normal property
Assert.That(outerPerson.Image.Length, Is.EqualTo(1)); // Lazy Property

// Changing the properties of the person in a different sessions
using (var innerSession = OpenSession())
{
var transaction = innerSession.BeginTransaction();

Person innerPerson = await (innerSession.CreateQuery(query).UniqueResultAsync<Person>());
innerPerson.Image = new byte[imageLength];
innerPerson.Name += namePostFix;
await (innerSession.UpdateAsync(innerPerson));

await (transaction.CommitAsync());
}

// Refreshing the person in the outer session
await (outerSession.RefreshAsync(outerPerson));

Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.True); // Value has changed
Assert.That(outerPerson.Image.Length, Is.EqualTo(imageLength)); // This is still the old value
}
}

private static Person GeneratePerson(int i, Person bestFriend)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1075,6 +1075,41 @@ void AssertPersons(List<Person> results, bool fetched)
}
}
}

[Test]
public void TestRefreshRemovesLazyLoadedProperties()
{
using (var outerSession = OpenSession())
{
const string query = "from Person fetch Image where Id = 1";
const string namePostFix = "_MODIFIED";
const int imageLength = 4711;

Person outerPerson = outerSession.CreateQuery(query).UniqueResult<Person>();

Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.False); // Normal property
Assert.That(outerPerson.Image.Length, Is.EqualTo(1)); // Lazy Property

// Changing the properties of the person in a different sessions
using (var innerSession = OpenSession())
{
var transaction = innerSession.BeginTransaction();

Person innerPerson = innerSession.CreateQuery(query).UniqueResult<Person>();
innerPerson.Image = new byte[imageLength];
innerPerson.Name += namePostFix;
innerSession.Update(innerPerson);

transaction.Commit();
}

// Refreshing the person in the outer session
outerSession.Refresh(outerPerson);

Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.True); // Value has changed
Assert.That(outerPerson.Image.Length, Is.EqualTo(imageLength)); // This is still the old value
}
}

private static Person GeneratePerson(int i, Person bestFriend)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ public virtual async Task OnRefreshAsync(RefreshEvent @event, IDictionary refres
}

await (EvictCachedCollectionsAsync(persister, id, source.Factory, cancellationToken)).ConfigureAwait(false);


RefreshLazyProperties(persister, obj);

// NH Different behavior : NH-1601
// At this point the entity need the real refresh, all elementes of collections are Refreshed,
// the collection state was evicted, but the PersistentCollection (in the entity state)
Expand Down
19 changes: 18 additions & 1 deletion src/NHibernate/Event/Default/DefaultRefreshEventListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready)
}

EvictCachedCollections(persister, id, source.Factory);


RefreshLazyProperties(persister, obj);

// NH Different behavior : NH-1601
// At this point the entity need the real refresh, all elementes of collections are Refreshed,
// the collection state was evicted, but the PersistentCollection (in the entity state)
Expand Down Expand Up @@ -142,5 +144,20 @@ private void EvictCachedCollections(IType[] types, object id, ISessionFactoryImp
}
}
}

private static void RefreshLazyProperties(IEntityPersister persister, object obj)
{
if (obj == null)
return;

// TODO: InstrumentationMetadata needs to be in IPersister
var castedPersister = persister as AbstractEntityPersister;
if (castedPersister?.InstrumentationMetadata?.EnhancedForLazyLoading == true)
{
var interceptor = castedPersister.InstrumentationMetadata.ExtractInterceptor(obj);
// The list of initialized lazy fields have to be cleared in order to refresh them from the database.
interceptor?.ClearInitializedLazyFields();
}
}
}
}
16 changes: 15 additions & 1 deletion src/NHibernate/Intercept/AbstractFieldInterceptor.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Iesi.Collections.Generic;
using NHibernate.Engine;
using NHibernate.Persister.Entity;
Expand All @@ -21,7 +22,8 @@ public abstract class AbstractFieldInterceptor : IFieldInterceptor
private readonly HashSet<string> loadedUnwrapProxyFieldNames = new HashSet<string>();
private readonly string entityName;
private readonly System.Type mappedClass;

private readonly string[] originalUninitializedFields;

[NonSerialized]
private bool initializing;
private bool isDirty;
Expand All @@ -34,6 +36,7 @@ protected internal AbstractFieldInterceptor(ISessionImplementor session, ISet<st
this.entityName = entityName;
this.mappedClass = mappedClass;
this.uninitializedFieldsReadOnly = uninitializedFields != null ? new ReadOnlySet<string>(uninitializedFields) : null;
this.originalUninitializedFields = uninitializedFields != null ? uninitializedFields.ToArray() : null;
}

#region IFieldInterceptor Members
Expand Down Expand Up @@ -209,5 +212,16 @@ public ISet<string> GetUninitializedFields()
{
return uninitializedFieldsReadOnly ?? CollectionHelper.EmptySet<string>();
}

public void ClearInitializedLazyFields()
{
if (this.originalUninitializedFields == null)
return;

foreach (var originalUninitializedField in this.originalUninitializedFields)
{
this.uninitializedFields.Add(originalUninitializedField);
}
}
}
}
2 changes: 2 additions & 0 deletions src/NHibernate/Intercept/IFieldInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public interface IFieldInterceptor

/// <summary> Get the MappedClass (field container).</summary>
System.Type MappedClass { get; }

void ClearInitializedLazyFields();
}

public static class FieldInterceptorExtensions
Expand Down