Skip to content

Commit ef5986e

Browse files
offanmaca88
authored andcommitted
Fix cast exception when merging a collection with a lazy property (#1992)
Fixes #1993 Co-authored-by: maca88 <bostjan.markezic@siol.net>
1 parent f3487e3 commit ef5986e

File tree

7 files changed

+172
-3
lines changed

7 files changed

+172
-3
lines changed

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010

1111
using System.Collections;
12+
using System.Collections.Generic;
1213
using System.Linq;
1314
using NHibernate.Cfg;
1415
using NHibernate.Intercept;
@@ -81,6 +82,7 @@ protected override void OnTearDown()
8182
using (var s = OpenSession())
8283
using (var tx = s.BeginTransaction())
8384
{
85+
s.CreateQuery("delete from Word").ExecuteUpdate();
8486
s.CreateQuery("delete from Book").ExecuteUpdate();
8587
tx.Commit();
8688
}
@@ -345,5 +347,56 @@ public async Task CacheShouldNotContainLazyPropertiesAsync()
345347
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "ALotOfText"), Is.False);
346348
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "Image"), Is.False);
347349
}
350+
351+
[Test]
352+
public async Task CanMergeTransientWithLazyPropertyInCollectionAsync()
353+
{
354+
Book book;
355+
356+
using (var s = OpenSession())
357+
using (var tx = s.BeginTransaction())
358+
{
359+
book = new Book
360+
{
361+
Name = "some name two",
362+
Id = 3,
363+
ALotOfText = "a lot of text two..."
364+
};
365+
// This should insert a new entity.
366+
await (s.MergeAsync(book));
367+
await (tx.CommitAsync());
368+
}
369+
370+
using (var s = OpenSession())
371+
{
372+
book = await (s.GetAsync<Book>(3));
373+
Assert.That(book, Is.Not.Null);
374+
Assert.That(book.Name, Is.EqualTo("some name two"));
375+
Assert.That(book.ALotOfText, Is.EqualTo("a lot of text two..."));
376+
377+
}
378+
using (var s = OpenSession())
379+
using (var tx = s.BeginTransaction())
380+
{
381+
book.Words = new List<Word>();
382+
var word = new Word
383+
{
384+
Id = 2,
385+
Parent = book,
386+
Content = new byte[1] {0}
387+
};
388+
389+
book.Words.Add(word);
390+
await (s.MergeAsync(book));
391+
await (tx.CommitAsync());
392+
}
393+
394+
using (var s = OpenSession())
395+
{
396+
book = await (s.GetAsync<Book>(3));
397+
Assert.That(book.Words.Any(), Is.True);
398+
Assert.That(book.Words.First().Content, Is.EqualTo(new byte[1] { 0 }));
399+
}
400+
}
348401
}
349402
}

src/NHibernate.Test/LazyProperty/Book.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace NHibernate.Test.LazyProperty
1+
using System.Collections.Generic;
2+
3+
namespace NHibernate.Test.LazyProperty
24
{
35
public class Book
46
{
@@ -16,5 +18,7 @@ public virtual string ALotOfText
1618
public virtual byte[] Image { get; set; }
1719

1820
public virtual string FieldInterceptor { get; set; }
21+
22+
public virtual IList<Word> Words { get; set; }
1923
}
2024
}

src/NHibernate.Test/LazyProperty/LazyPropertyFixture.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using NHibernate.Cfg;
45
using NHibernate.Intercept;
@@ -68,6 +69,7 @@ protected override void OnTearDown()
6869
using (var s = OpenSession())
6970
using (var tx = s.BeginTransaction())
7071
{
72+
s.CreateQuery("delete from Word").ExecuteUpdate();
7173
s.CreateQuery("delete from Book").ExecuteUpdate();
7274
tx.Commit();
7375
}
@@ -338,5 +340,56 @@ public void CacheShouldNotContainLazyProperties()
338340
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "ALotOfText"), Is.False);
339341
Assert.That(NHibernateUtil.IsPropertyInitialized(book, "Image"), Is.False);
340342
}
343+
344+
[Test]
345+
public void CanMergeTransientWithLazyPropertyInCollection()
346+
{
347+
Book book;
348+
349+
using (var s = OpenSession())
350+
using (var tx = s.BeginTransaction())
351+
{
352+
book = new Book
353+
{
354+
Name = "some name two",
355+
Id = 3,
356+
ALotOfText = "a lot of text two..."
357+
};
358+
// This should insert a new entity.
359+
s.Merge(book);
360+
tx.Commit();
361+
}
362+
363+
using (var s = OpenSession())
364+
{
365+
book = s.Get<Book>(3);
366+
Assert.That(book, Is.Not.Null);
367+
Assert.That(book.Name, Is.EqualTo("some name two"));
368+
Assert.That(book.ALotOfText, Is.EqualTo("a lot of text two..."));
369+
370+
}
371+
using (var s = OpenSession())
372+
using (var tx = s.BeginTransaction())
373+
{
374+
book.Words = new List<Word>();
375+
var word = new Word
376+
{
377+
Id = 2,
378+
Parent = book,
379+
Content = new byte[1] {0}
380+
};
381+
382+
book.Words.Add(word);
383+
s.Merge(book);
384+
tx.Commit();
385+
}
386+
387+
using (var s = OpenSession())
388+
{
389+
book = s.Get<Book>(3);
390+
Assert.That(book.Words.Any(), Is.True);
391+
Assert.That(book.Words.First().Content, Is.EqualTo(new byte[1] { 0 }));
392+
}
393+
}
341394
}
342395
}

src/NHibernate.Test/LazyProperty/Mappings.hbm.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@
1212
<property name="ALotOfText" lazy="true" />
1313
<property name="Image" lazy="true" />
1414
<property name="FieldInterceptor" />
15+
<bag name="Words" inverse="true" generic="true" cascade="all-delete-orphan" lazy="true" >
16+
<key column="ParentId" />
17+
<one-to-many class="Word" />
18+
</bag>
1519
</class>
1620

21+
<class name="Word">
22+
<id name="Id" column="Id">
23+
<generator class="assigned" />
24+
</id>
25+
<property name="Content" lazy="true" />
26+
<many-to-one name="Parent" class="Book" column="ParentId" />
27+
</class>
28+
1729
</hibernate-mapping>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace NHibernate.Test.LazyProperty
2+
{
3+
public class Word
4+
{
5+
public virtual int Id { get; set; }
6+
public virtual byte[] Content { get; set; }
7+
public virtual Book Parent { get; set; }
8+
}
9+
}

src/NHibernate/Async/Type/TypeHelper.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,25 @@ public static async Task<object[]> ReplaceAsync(object[] original, object[] targ
169169
{
170170
copied[i] = target[i];
171171
}
172+
else if (target[i] == LazyPropertyInitializer.UnfetchedProperty)
173+
{
174+
// Should be no need to check for target[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN
175+
// because PropertyAccessStrategyBackRefImpl.get( object ) returns
176+
// PropertyAccessStrategyBackRefImpl.UNKNOWN, so target[i] == original[i].
177+
//
178+
// We know from above that original[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY &&
179+
// original[i] != PropertyAccessStrategyBackRefImpl.UNKNOWN;
180+
// This is a case where the entity being merged has a lazy property
181+
// that has been initialized. Copy the initialized value from original.
182+
if (types[i].IsMutable)
183+
{
184+
copied[i] = types[i].DeepCopy(original[i], session.Factory);
185+
}
186+
else
187+
{
188+
copied[i] = original[i];
189+
}
190+
}
172191
else
173192
copied[i] = await (types[i].ReplaceAsync(original[i], target[i], session, owner, copyCache, foreignKeyDirection, cancellationToken)).ConfigureAwait(false);
174193
}
@@ -343,4 +362,4 @@ public static async Task<int[]> FindModifiedAsync(StandardProperty[] properties,
343362
}
344363
}
345364
}
346-
}
365+
}

src/NHibernate/Type/TypeHelper.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,25 @@ public static object[] Replace(object[] original, object[] target, IType[] types
175175
{
176176
copied[i] = target[i];
177177
}
178+
else if (target[i] == LazyPropertyInitializer.UnfetchedProperty)
179+
{
180+
// Should be no need to check for target[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN
181+
// because PropertyAccessStrategyBackRefImpl.get( object ) returns
182+
// PropertyAccessStrategyBackRefImpl.UNKNOWN, so target[i] == original[i].
183+
//
184+
// We know from above that original[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY &&
185+
// original[i] != PropertyAccessStrategyBackRefImpl.UNKNOWN;
186+
// This is a case where the entity being merged has a lazy property
187+
// that has been initialized. Copy the initialized value from original.
188+
if (types[i].IsMutable)
189+
{
190+
copied[i] = types[i].DeepCopy(original[i], session.Factory);
191+
}
192+
else
193+
{
194+
copied[i] = original[i];
195+
}
196+
}
178197
else
179198
copied[i] = types[i].Replace(original[i], target[i], session, owner, copyCache, foreignKeyDirection);
180199
}
@@ -342,4 +361,4 @@ public static int[] FindModified(StandardProperty[] properties,
342361
}
343362
}
344363
}
345-
}
364+
}

0 commit comments

Comments
 (0)