Skip to content

Commit 9130217

Browse files
Fix a stack overflow when merging a transient entity with lazy properties
* 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.
1 parent ee89456 commit 9130217

File tree

3 files changed

+90
-28
lines changed

3 files changed

+90
-28
lines changed

src/NHibernate.Test/Async/LazyProperty/LazyPropertyFixture.cs

+46-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99

1010

1111
using System.Collections;
12+
using System.Linq;
1213
using NHibernate.Intercept;
1314
using NHibernate.Tuple.Entity;
1415
using NUnit.Framework;
16+
using NHibernate.Linq;
1517

1618
namespace NHibernate.Test.LazyProperty
1719
{
@@ -68,7 +70,7 @@ protected override void OnTearDown()
6870
using (var s = OpenSession())
6971
using (var tx = s.BeginTransaction())
7072
{
71-
Assert.That(s.CreateSQLQuery("delete from Book").ExecuteUpdate(), Is.EqualTo(1));
73+
s.CreateQuery("delete from Book").ExecuteUpdate();
7274
tx.Commit();
7375
}
7476
}
@@ -137,14 +139,25 @@ public async Task CanGetValueForNonLazyPropertyAsync()
137139
public async Task CanLoadAndSaveObjectInDifferentSessionsAsync()
138140
{
139141
Book book;
142+
int bookCount;
140143
using (ISession s = OpenSession())
141144
{
145+
bookCount = await (s.Query<Book>().CountAsync());
142146
book = await (s.GetAsync<Book>(1));
143147
}
144148

149+
book.Name += " updated";
150+
145151
using (ISession s = OpenSession())
146152
{
147153
await (s.MergeAsync(book));
154+
await (s.FlushAsync());
155+
}
156+
157+
using (ISession s = OpenSession())
158+
{
159+
Assert.That(await (s.Query<Book>().CountAsync()), Is.EqualTo(bookCount));
160+
Assert.That((await (s.GetAsync<Book>(1))).Name, Is.EqualTo(book.Name));
148161
}
149162
}
150163

@@ -171,5 +184,37 @@ public async Task CanUpdateNonLazyWithoutLoadingLazyPropertyAsync()
171184
Assert.That(book.FieldInterceptor, Is.EqualTo("Why not that name?updated"));
172185
}
173186
}
187+
188+
[Test]
189+
public async Task CanMergeTransientWithLazyPropertyAsync()
190+
{
191+
using (ISession s = OpenSession())
192+
{
193+
var book = await (s.GetAsync<Book>(2));
194+
Assert.That(book, Is.Null);
195+
}
196+
197+
using (ISession s = OpenSession())
198+
using (var tx = s.BeginTransaction())
199+
{
200+
var book = new Book
201+
{
202+
Name = "some name two",
203+
Id = 2,
204+
ALotOfText = "a lot of text two..."
205+
};
206+
// This should insert a new entity.
207+
await (s.MergeAsync(book));
208+
await (tx.CommitAsync());
209+
}
210+
211+
using (ISession s = OpenSession())
212+
{
213+
var book = await (s.GetAsync<Book>(2));
214+
Assert.That(book, Is.Not.Null);
215+
Assert.That(book.Name, Is.EqualTo("some name two"));
216+
Assert.That(book.ALotOfText, Is.EqualTo("a lot of text two..."));
217+
}
218+
}
174219
}
175220
}

src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections;
2+
using System.Linq;
23
using NHibernate.Intercept;
34
using NHibernate.Tuple.Entity;
45
using NUnit.Framework;
@@ -57,7 +58,7 @@ protected override void OnTearDown()
5758
using (var s = OpenSession())
5859
using (var tx = s.BeginTransaction())
5960
{
60-
Assert.That(s.CreateSQLQuery("delete from Book").ExecuteUpdate(), Is.EqualTo(1));
61+
s.CreateQuery("delete from Book").ExecuteUpdate();
6162
tx.Commit();
6263
}
6364
}
@@ -132,14 +133,25 @@ public void CanGetValueForNonLazyProperty()
132133
public void CanLoadAndSaveObjectInDifferentSessions()
133134
{
134135
Book book;
136+
int bookCount;
135137
using (ISession s = OpenSession())
136138
{
139+
bookCount = s.Query<Book>().Count();
137140
book = s.Get<Book>(1);
138141
}
139142

143+
book.Name += " updated";
144+
140145
using (ISession s = OpenSession())
141146
{
142147
s.Merge(book);
148+
s.Flush();
149+
}
150+
151+
using (ISession s = OpenSession())
152+
{
153+
Assert.That(s.Query<Book>().Count(), Is.EqualTo(bookCount));
154+
Assert.That(s.Get<Book>(1).Name, Is.EqualTo(book.Name));
143155
}
144156
}
145157

@@ -168,24 +180,34 @@ public void CanUpdateNonLazyWithoutLoadingLazyProperty()
168180
}
169181

170182
[Test]
171-
public void CanMergeWithLazyProperty()
183+
public void CanMergeTransientWithLazyProperty()
172184
{
185+
using (ISession s = OpenSession())
186+
{
187+
var book = s.Get<Book>(2);
188+
Assert.That(book, Is.Null);
189+
}
190+
173191
using (ISession s = OpenSession())
174192
using (var tx = s.BeginTransaction())
175193
{
176194
var book = new Book
177195
{
178196
Name = "some name two",
179197
Id = 2,
180-
ALotOfText = "a lot of text ..."
198+
ALotOfText = "a lot of text two..."
181199
};
200+
// This should insert a new entity.
182201
s.Merge(book);
183202
tx.Commit();
184203
}
204+
185205
using (ISession s = OpenSession())
186206
{
187207
var book = s.Get<Book>(2);
188208
Assert.That(book, Is.Not.Null);
209+
Assert.That(book.Name, Is.EqualTo("some name two"));
210+
Assert.That(book.ALotOfText, Is.EqualTo("a lot of text two..."));
189211
}
190212
}
191213
}

src/NHibernate/Intercept/DefaultDynamicLazyFieldInterceptor.cs

+19-24
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,15 @@ public class DefaultDynamicLazyFieldInterceptor : IFieldInterceptorAccessor, Pro
1212

1313
public object Intercept(InvocationInfo info)
1414
{
15-
var methodName = info.TargetMethod.Name;
16-
if (FieldInterceptor != null)
15+
if (ReflectHelper.IsPropertyGet(info.TargetMethod))
1716
{
18-
if (ReflectHelper.IsPropertyGet(info.TargetMethod))
17+
if (IsGetFieldInterceptorCall(info.TargetMethod))
1918
{
20-
if (IsGetFieldInterceptorCall(methodName, info.TargetMethod))
21-
{
22-
return FieldInterceptor;
23-
}
19+
return FieldInterceptor;
20+
}
2421

22+
if (FieldInterceptor != null)
23+
{
2524
object propValue = info.InvokeMethodOnTarget();
2625

2726
var result = FieldInterceptor.Intercept(info.Target, ReflectHelper.GetPropertyName(info.TargetMethod), propValue);
@@ -31,37 +30,33 @@ public object Intercept(InvocationInfo info)
3130
return result;
3231
}
3332
}
34-
else if (ReflectHelper.IsPropertySet(info.TargetMethod))
35-
{
36-
if (IsSetFieldInterceptorCall(methodName, info.TargetMethod))
37-
{
38-
FieldInterceptor = (IFieldInterceptor)info.Arguments[0];
39-
return null;
40-
}
41-
FieldInterceptor.MarkDirty();
42-
FieldInterceptor.Intercept(info.Target, ReflectHelper.GetPropertyName(info.TargetMethod), info.Arguments[0]);
43-
}
4433
}
45-
else
34+
else if (ReflectHelper.IsPropertySet(info.TargetMethod))
4635
{
47-
if (IsSetFieldInterceptorCall(methodName, info.TargetMethod))
36+
if (IsSetFieldInterceptorCall(info.TargetMethod))
4837
{
49-
FieldInterceptor = (IFieldInterceptor)info.Arguments[0];
38+
FieldInterceptor = (IFieldInterceptor) info.Arguments[0];
5039
return null;
5140
}
41+
42+
if (FieldInterceptor != null)
43+
{
44+
FieldInterceptor.MarkDirty();
45+
FieldInterceptor.Intercept(info.Target, ReflectHelper.GetPropertyName(info.TargetMethod), info.Arguments[0]);
46+
}
5247
}
5348

5449
return info.InvokeMethodOnTarget();
5550
}
5651

57-
private static bool IsGetFieldInterceptorCall(string methodName, MethodInfo targetMethod)
52+
private static bool IsGetFieldInterceptorCall(MethodInfo targetMethod)
5853
{
59-
return "get_FieldInterceptor".Equals(methodName) && targetMethod.DeclaringType == typeof(IFieldInterceptorAccessor);
54+
return string.Equals("get_FieldInterceptor", targetMethod.Name, StringComparison.Ordinal) && targetMethod.DeclaringType == typeof(IFieldInterceptorAccessor);
6055
}
6156

62-
private static bool IsSetFieldInterceptorCall(string methodName, MethodInfo targetMethod)
57+
private static bool IsSetFieldInterceptorCall(MethodInfo targetMethod)
6358
{
64-
return "set_FieldInterceptor".Equals(methodName) && targetMethod.DeclaringType == typeof(IFieldInterceptorAccessor);
59+
return string.Equals("set_FieldInterceptor", targetMethod.Name, StringComparison.Ordinal) && targetMethod.DeclaringType == typeof(IFieldInterceptorAccessor);
6560
}
6661
}
6762
}

0 commit comments

Comments
 (0)