-
Notifications
You must be signed in to change notification settings - Fork 934
Fix StackOverflowException in DefaultDynamicLazyFieldInterceptor #1437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
fixed the test |
@@ -50,7 +50,7 @@ protected override void OnTearDown() | |||
using (var s = OpenSession()) | |||
using (var tx = s.BeginTransaction()) | |||
{ | |||
Assert.That(s.CreateSQLQuery("delete from Book").ExecuteUpdate(), Is.EqualTo(1)); | |||
Assert.That(s.CreateSQLQuery("delete from Book").ExecuteUpdate(), Is.EqualTo(2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since I have commented on the commit (no PR at that time), the PR was not showing the line I was commenting. This was this one.
I consider it should be changed to:
s.CreateQuery("delete from Book").ExecuteUpdate();
There are no reason to assert the number of inserted things in the teardown. So better remove this undue old assert rather than fixing it. (By the way, it is better to use a HQL delete rather than a SQL one, so I have changed that too in the above code.)
|
||
[Test] | ||
public void CanMergeWithLazyProperty() | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting glitch.
fixed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have adjusted the tests, and I have added a potential fix. I think there is still some points to check, see comments details.
@@ -50,7 +51,7 @@ protected override void OnTearDown() | |||
using (var s = OpenSession()) | |||
using (var tx = s.BeginTransaction()) | |||
{ | |||
Assert.That(s.CreateSQLQuery("delete from Book").ExecuteUpdate(), Is.EqualTo(1)); | |||
s.CreateQuery("delete from Book").ExecuteUpdate(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assert in teardown is indeed "completing" the CanLoadAndSaveObjectInDifferentSessions
test. Removed and CanLoadAndSaveObjectInDifferentSessions
test changed for correctly asserting itself all that it needs to assert.
using (ISession s = OpenSession()) | ||
{ | ||
var book = s.Get<Book>(2); | ||
Assert.That(book, Is.Null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test fails when merging a not yet persisted entity. Since we use assigned ids, we must ensure that any future changes will not cause this test to start merging an already persisted entity.
@@ -230,6 +230,9 @@ private object MergeTransientEntity(object entity, string entityName, object req | |||
else | |||
{ | |||
copy = source.Instantiate(persister, id); | |||
// #1436: we use the persister to instanciate a new, transient entity. In case it has lazy properties, | |||
// we need to to tell its proxy that the properties are all fetched, since it is a transient entity. | |||
persister.AfterInitialize(copy, false, source); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minimal fix. I wonder whether it should be put here or inside SessionImpl.Instantiate
called just above. The Instantiate
method semantic does not seem to imply it may only be used on transient entities, that is why I have decided to put the fix here.
The stack overflow was occurring because the proxy FieldInterceptor
was not initialized. In such case, trying to get it causes a sort of "endless self initialization" loop. Maybe I should try to fix that instead. I will give it a try later.
|
||
if ("get_FieldInterceptor".Equals(methodName)) | ||
{ | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the field interceptor is not set (we are here in the else
part of an if (FieldInterceptor != null)
) and is asked for, we should just yield null
instead of trying to call it on the underlying Target
(which was causing the stack overflow).
The "get_FieldInterceptor".Equals(methodName)
test is debatable and causes issues if the entity itself has a FieldInterceptor
property. But this case is already bugged due to other similar tests done in DefaultDynamicLazyFieldInterceptor
, so adding it does not cause any regression. It will need a separated fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think checks for set_FieldInterceptor
/get_FieldInterceptor
shall be at the beginning of the method, as in anyway the code inside is a duplicate.
@@ -230,9 +230,6 @@ private object MergeTransientEntity(object entity, string entityName, object req | |||
else | |||
{ | |||
copy = source.Instantiate(persister, id); | |||
// #1436: we use the persister to instanciate a new, transient entity. In case it has lazy properties, | |||
// we need to to tell its proxy that the properties are all fetched, since it is a transient entity. | |||
persister.AfterInitialize(copy, false, source); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix no more needed with the alternate fix. Not having the proxy initialized is then equivalent to having it considering all properties are already fetched.
Ascertained by adding more asserts in the transient merge test.
@hazzik, if the refactoring is ok for you, I am going to rebase and squash all that to two commits, by author. |
@@ -13,15 +13,25 @@ public class DefaultDynamicLazyFieldInterceptor : IFieldInterceptorAccessor, Pro | |||
public object Intercept(InvocationInfo info) | |||
{ | |||
var methodName = info.TargetMethod.Name; | |||
if (FieldInterceptor != null) | |||
var isPropertyGet = ReflectHelper.IsPropertyGet(info.TargetMethod); | |||
var isPropertySet = !isPropertyGet && ReflectHelper.IsPropertySet(info.TargetMethod); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!isPropertyGet &&
part isn't needed
@fredericDelaporte I did a further refactoring and merged master into here. If you want to squash commits, I don't mind if my name would be lost in the process. |
The point on names is mainly to keep commit associated with who has directly worked on the change. For the fix part, being you or me, no trouble, but I do not think it should be @offan since he may not have analyzed and followed the rational behind the fix. So if someone was later asking him explanations on the fix if he was set as the author, it would not be practical. |
…ties * Fixes #1436 * Fixes a test which was relying on an assert in teardown. * Adjusts the new Merge test to ensure it tests its expected case.
Rebased and squashed. |
Fixes #1436