Skip to content

Commit b734d00

Browse files
picbeatsbahusoid
authored andcommitted
Fix NullReferenceException in EntityEntry.GetLoadedValue
Fixes #2137 Co-authored-by: Roman Artiukhin <bahusdrive@gmail.com>
1 parent a05b336 commit b734d00

File tree

6 files changed

+150
-2
lines changed

6 files changed

+150
-2
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using NUnit.Framework;
12+
13+
namespace NHibernate.Test.NHSpecificTest.GH2137
14+
{
15+
using System.Threading.Tasks;
16+
[TestFixture]
17+
public class FixtureAsync : BugTestCase
18+
{
19+
private Entity e1;
20+
21+
protected override void OnSetUp()
22+
{
23+
using (var session = OpenSession())
24+
using (var transaction = session.BeginTransaction())
25+
{
26+
e1 = new Entity {Name = "Bob"};
27+
session.Save(e1);
28+
29+
transaction.Commit();
30+
}
31+
}
32+
33+
protected override void OnTearDown()
34+
{
35+
using (var session = OpenSession())
36+
using (var transaction = session.BeginTransaction())
37+
{
38+
// The HQL delete does all the job inside the database without loading the entities, but it does
39+
// not handle delete order for avoiding violating constraints if any. Use
40+
// session.Delete("from System.Object");
41+
// instead if in need of having NHibernate ordering the deletes, but this will cause
42+
// loading the entities in the session.
43+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
44+
45+
transaction.Commit();
46+
}
47+
}
48+
49+
[Test]
50+
public async Task TestUpdateDetachedEntityAsync()
51+
{
52+
using (var session = OpenSession())
53+
using (var transaction = session.BeginTransaction())
54+
{
55+
e1.Name = "Sally";
56+
await (session.UpdateAsync(e1));
57+
await (transaction.CommitAsync());
58+
}
59+
}
60+
}
61+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2137
4+
{
5+
class Entity
6+
{
7+
public virtual Guid Id { get; set; }
8+
public virtual string Name { get; set; }
9+
public virtual ExtendedProperties ExtendedProperties { get; set; }
10+
}
11+
12+
class ExtendedProperties
13+
{
14+
public virtual Guid Id { get; set; }
15+
16+
public virtual string Value { get; set; }
17+
}
18+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using NUnit.Framework;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2137
4+
{
5+
[TestFixture]
6+
public class Fixture : BugTestCase
7+
{
8+
private Entity e1;
9+
10+
protected override void OnSetUp()
11+
{
12+
using (var session = OpenSession())
13+
using (var transaction = session.BeginTransaction())
14+
{
15+
e1 = new Entity {Name = "Bob"};
16+
session.Save(e1);
17+
18+
transaction.Commit();
19+
}
20+
}
21+
22+
protected override void OnTearDown()
23+
{
24+
using (var session = OpenSession())
25+
using (var transaction = session.BeginTransaction())
26+
{
27+
// The HQL delete does all the job inside the database without loading the entities, but it does
28+
// not handle delete order for avoiding violating constraints if any. Use
29+
// session.Delete("from System.Object");
30+
// instead if in need of having NHibernate ordering the deletes, but this will cause
31+
// loading the entities in the session.
32+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
33+
34+
transaction.Commit();
35+
}
36+
}
37+
38+
[Test]
39+
public void TestUpdateDetachedEntity()
40+
{
41+
using (var session = OpenSession())
42+
using (var transaction = session.BeginTransaction())
43+
{
44+
e1.Name = "Sally";
45+
session.Update(e1);
46+
transaction.Commit();
47+
}
48+
}
49+
}
50+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
3+
namespace="NHibernate.Test.NHSpecificTest.GH2137">
4+
5+
<class name="Entity">
6+
<id name="Id" generator="guid.comb"/>
7+
<property name="Name"/>
8+
<many-to-one name="ExtendedProperties" class="ExtendedProperties" unique="true" cascade="all-delete-orphan" lazy="proxy" />
9+
10+
</class>
11+
12+
<class name="ExtendedProperties">
13+
<id name="Id" generator="guid.comb"/>
14+
<property name="Value" column="`Value`"/>
15+
</class>
16+
17+
</hibernate-mapping>

src/NHibernate/Async/Engine/Cascade.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ private async Task CascadePropertyAsync(object parent, object child, IType type,
117117
// value is orphaned if loaded state for this property shows not null
118118
// because it is currently null.
119119
EntityEntry entry = eventSource.PersistenceContext.GetEntry(parent);
120-
if (entry != null && entry.Status != Status.Saving)
120+
//LoadedState is null when detached entity is cascaded from session.Update context
121+
if (entry?.LoadedState != null && entry.Status != Status.Saving)
121122
{
122123
object loadedValue;
123124
if (componentPathStack.Count == 0)

src/NHibernate/Engine/Cascade.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ private void CascadeProperty(object parent, object child, IType type, CascadeSty
169169
// value is orphaned if loaded state for this property shows not null
170170
// because it is currently null.
171171
EntityEntry entry = eventSource.PersistenceContext.GetEntry(parent);
172-
if (entry != null && entry.Status != Status.Saving)
172+
//LoadedState is null when detached entity is cascaded from session.Update context
173+
if (entry?.LoadedState != null && entry.Status != Status.Saving)
173174
{
174175
object loadedValue;
175176
if (componentPathStack.Count == 0)

0 commit comments

Comments
 (0)