ISession.Refresh does not update lazy properties
Hi,
we have noticed a problem in versions 4.4.9 and 5.5.2. In version 5.2.1 we did not have this problem, I suspect it is related to the lazy property changes in version 5.3 (https://github.com/nhibernate/nhibernate-core/commit/116398d6d16b1f1415285936aab07c98508d2149).
The following scenario: We have opened a session with objects and also open another session and edit an object there, which is already loaded in the first session. We call Refresh so that this object is up-to-date in the previous session. Unfortunately, in the versions above, the lazy properties are no longer updated, but contain the previous and therefore old value.
Reproduction:
[Test] // In Class FetchLazyPropertiesFixture
public void Bug()
{
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
}
}
Best regards, Thomas
Hello again,
I found out that if I always inject a new interceptor in the method PocoEntityTuplizer.AfterInitialize, my test runs through. This will certainly not be the solution, but I hope it can help?
public override void AfterInitialize(object entity, ISessionImplementor session)
{
if (IsInstrumented)
{
// var interceptor = _enhancementMetadata.ExtractInterceptor(entity);
// if (interceptor == null)
// {
// interceptor = _enhancementMetadata.InjectInterceptor(entity, session);
// }
// else
// {
// interceptor.Session = session;
// }
var interceptor = _enhancementMetadata.InjectInterceptor(entity, session);
interceptor?.ClearDirty();
}
}
Best regards, Thomas
Hi,
i have created a draft patch file, which should fix the problem. The problem is, that i am using some obsolete methods... But maybe this can help to find a better fix.
Bugfix_Issue_3622.patch The patch is created for Version 4.4.9
Best regards, Thomas
After looking at the patch, instead of creating a new issue, I think it is appropriate for me to add on here that I don't believe this is isolated to lazy properties or stateful sessions. I think the Refresh method is just broke for both ISession and IStatelessSession.
This simple test case fails on the last line as the entity value is still 1:
var myEntity1 = statelessSession1.Get<MyEntity>(101);
var myEntity2 = statelessSession2.Get<MyEntity>(101);
myEntity1.Value = 1;
statelessSession1.Update(myEntity1);
statelessSession2.Refresh(myEntity2);
Assert.Equal(1, myEntity2.Value);
myEntity1.Value = 2;
statelessSession1.Update(myEntity1);
statelessSession2.Refresh(myEntity2);
Assert.Equal(2, myEntity2.Value);
I've debugged it down through almost the exact same code path as the above patch. It appears Loader.cs is issuing the query to the db, but then it get snagged in the GetRow method as its key being already loaded, thus bypassing binding/hydration.
Great job on adding a patch @ValResnick !
EDIT: NH 5.5.2 / Fluent NH 3.4.0
EDIT 2: My mistake, the async version of Refresh appears to be having the same behavior.
Was fixed in Hibernate here: https://github.com/hibernate/hibernate-orm/commit/4ebbf5b36da9fd9b6cee263d551e63c95c9ec349 I would prefer to have the similar implementation
I have adapted the Hibernate commit mentioned above: https://github.com/nhibernate/nhibernate-core/pull/3697