Skip to content

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

Merged
merged 2 commits into from
Nov 19, 2017
Merged
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
47 changes: 46 additions & 1 deletion src/NHibernate.Test/Async/LazyProperty/LazyPropertyFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@


using System.Collections;
using System.Linq;
using NHibernate.Intercept;
using NHibernate.Tuple.Entity;
using NUnit.Framework;
using NHibernate.Linq;

namespace NHibernate.Test.LazyProperty
{
Expand Down Expand Up @@ -68,7 +70,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();
tx.Commit();
}
}
Expand Down Expand Up @@ -137,14 +139,25 @@ public async Task CanGetValueForNonLazyPropertyAsync()
public async Task CanLoadAndSaveObjectInDifferentSessionsAsync()
{
Book book;
int bookCount;
using (ISession s = OpenSession())
{
bookCount = await (s.Query<Book>().CountAsync());
book = await (s.GetAsync<Book>(1));
}

book.Name += " updated";

using (ISession s = OpenSession())
{
await (s.MergeAsync(book));
await (s.FlushAsync());
}

using (ISession s = OpenSession())
{
Assert.That(await (s.Query<Book>().CountAsync()), Is.EqualTo(bookCount));
Assert.That((await (s.GetAsync<Book>(1))).Name, Is.EqualTo(book.Name));
}
}

Expand All @@ -171,5 +184,37 @@ public async Task CanUpdateNonLazyWithoutLoadingLazyPropertyAsync()
Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?updated"));
}
}

[Test]
public async Task CanMergeTransientWithLazyPropertyAsync()
{
using (ISession s = OpenSession())
{
var book = await (s.GetAsync<Book>(2));
Assert.That(book, Is.Null);
}

using (ISession s = OpenSession())
using (var tx = s.BeginTransaction())
{
var book = new Book
{
Name = "some name two",
Id = 2,
ALotOfText = "a lot of text two..."
};
// This should insert a new entity.
await (s.MergeAsync(book));
await (tx.CommitAsync());
}

using (ISession s = OpenSession())
{
var book = await (s.GetAsync<Book>(2));
Assert.That(book, Is.Not.Null);
Assert.That(book.Name, Is.EqualTo("some name two"));
Assert.That(book.ALotOfText, Is.EqualTo("a lot of text two..."));
}
}
}
}
46 changes: 45 additions & 1 deletion src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System.Linq;
using NHibernate.Intercept;
using NHibernate.Tuple.Entity;
using NUnit.Framework;
Expand Down Expand Up @@ -57,7 +58,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();
Copy link
Member

@fredericDelaporte fredericDelaporte Nov 17, 2017

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.

tx.Commit();
}
}
Expand Down Expand Up @@ -132,14 +133,25 @@ public void CanGetValueForNonLazyProperty()
public void CanLoadAndSaveObjectInDifferentSessions()
{
Book book;
int bookCount;
using (ISession s = OpenSession())
{
bookCount = s.Query<Book>().Count();
book = s.Get<Book>(1);
}

book.Name += " updated";

using (ISession s = OpenSession())
{
s.Merge(book);
s.Flush();
}

using (ISession s = OpenSession())
{
Assert.That(s.Query<Book>().Count(), Is.EqualTo(bookCount));
Assert.That(s.Get<Book>(1).Name, Is.EqualTo(book.Name));
}
}

Expand All @@ -166,5 +178,37 @@ public void CanUpdateNonLazyWithoutLoadingLazyProperty()
Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?updated"));
}
}

[Test]
public void CanMergeTransientWithLazyProperty()
{
using (ISession s = OpenSession())
{
var book = s.Get<Book>(2);
Assert.That(book, Is.Null);
}

using (ISession s = OpenSession())
using (var tx = s.BeginTransaction())
{
var book = new Book
{
Name = "some name two",
Id = 2,
ALotOfText = "a lot of text two..."
};
// This should insert a new entity.
s.Merge(book);
tx.Commit();
}

using (ISession s = OpenSession())
{
var book = s.Get<Book>(2);
Assert.That(book, Is.Not.Null);
Assert.That(book.Name, Is.EqualTo("some name two"));
Assert.That(book.ALotOfText, Is.EqualTo("a lot of text two..."));
}
}
}
}
43 changes: 19 additions & 24 deletions src/NHibernate/Intercept/DefaultDynamicLazyFieldInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ public class DefaultDynamicLazyFieldInterceptor : IFieldInterceptorAccessor, Pro

public object Intercept(InvocationInfo info)
{
var methodName = info.TargetMethod.Name;
if (FieldInterceptor != null)
if (ReflectHelper.IsPropertyGet(info.TargetMethod))
{
if (ReflectHelper.IsPropertyGet(info.TargetMethod))
if (IsGetFieldInterceptorCall(info.TargetMethod))
{
if (IsGetFieldInterceptorCall(methodName, info.TargetMethod))
{
return FieldInterceptor;
}
return FieldInterceptor;
}

if (FieldInterceptor != null)
{
object propValue = info.InvokeMethodOnTarget();

var result = FieldInterceptor.Intercept(info.Target, ReflectHelper.GetPropertyName(info.TargetMethod), propValue);
Expand All @@ -31,37 +30,33 @@ public object Intercept(InvocationInfo info)
return result;
}
}
else if (ReflectHelper.IsPropertySet(info.TargetMethod))
{
if (IsSetFieldInterceptorCall(methodName, info.TargetMethod))
{
FieldInterceptor = (IFieldInterceptor)info.Arguments[0];
return null;
}
FieldInterceptor.MarkDirty();
FieldInterceptor.Intercept(info.Target, ReflectHelper.GetPropertyName(info.TargetMethod), info.Arguments[0]);
}
}
else
else if (ReflectHelper.IsPropertySet(info.TargetMethod))
{
if (IsSetFieldInterceptorCall(methodName, info.TargetMethod))
if (IsSetFieldInterceptorCall(info.TargetMethod))
{
FieldInterceptor = (IFieldInterceptor)info.Arguments[0];
FieldInterceptor = (IFieldInterceptor) info.Arguments[0];
return null;
}

if (FieldInterceptor != null)
{
FieldInterceptor.MarkDirty();
FieldInterceptor.Intercept(info.Target, ReflectHelper.GetPropertyName(info.TargetMethod), info.Arguments[0]);
}
}

return info.InvokeMethodOnTarget();
}

private static bool IsGetFieldInterceptorCall(string methodName, MethodInfo targetMethod)
private static bool IsGetFieldInterceptorCall(MethodInfo targetMethod)
{
return "get_FieldInterceptor".Equals(methodName) && targetMethod.DeclaringType == typeof(IFieldInterceptorAccessor);
return string.Equals("get_FieldInterceptor", targetMethod.Name, StringComparison.Ordinal) && targetMethod.DeclaringType == typeof(IFieldInterceptorAccessor);
}

private static bool IsSetFieldInterceptorCall(string methodName, MethodInfo targetMethod)
private static bool IsSetFieldInterceptorCall(MethodInfo targetMethod)
{
return "set_FieldInterceptor".Equals(methodName) && targetMethod.DeclaringType == typeof(IFieldInterceptorAccessor);
return string.Equals("set_FieldInterceptor", targetMethod.Name, StringComparison.Ordinal) && targetMethod.DeclaringType == typeof(IFieldInterceptorAccessor);
}
}
}