Skip to content

Commit 6cd55ae

Browse files
committed
Merge branch 'master' into net10
2 parents 2810e33 + 86e05bf commit 6cd55ae

File tree

13 files changed

+440
-6
lines changed

13 files changed

+440
-6
lines changed

doc/reference/modules/configuration.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,6 +1606,11 @@ in the parameter binding.</programlisting>
16061606
<entry><literal>NHibernate.Dialect.SybaseASE15Dialect</literal></entry>
16071607
<entry></entry>
16081608
</row>
1609+
<row>
1610+
<entry>Sybase Adaptive Server Enterprise 16</entry>
1611+
<entry><literal>NHibernate.Dialect.SybaseASE16Dialect</literal></entry>
1612+
<entry></entry>
1613+
</row>
16091614
<row>
16101615
<entry>Sybase SQL Anywhere 10</entry>
16111616
<entry><literal>NHibernate.Dialect.SybaseSQLAnywhere10Dialect</literal></entry>

src/NHibernate.Test/Async/FetchLazyProperties/FetchLazyPropertiesFixture.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,41 @@ void AssertPersons(List<Person> results, bool fetched)
10861086
}
10871087
}
10881088
}
1089+
1090+
[Test]
1091+
public async Task TestRefreshRemovesLazyLoadedPropertiesAsync()
1092+
{
1093+
using (var outerSession = OpenSession())
1094+
{
1095+
const string query = "from Person fetch Image where Id = 1";
1096+
const string namePostFix = "_MODIFIED";
1097+
const int imageLength = 1985;
1098+
1099+
Person outerPerson = await (outerSession.CreateQuery(query).UniqueResultAsync<Person>());
1100+
1101+
Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.False); // Normal property
1102+
Assert.That(outerPerson.Image.Length, Is.EqualTo(1)); // Lazy Property
1103+
1104+
// Changing the properties of the person in a different sessions
1105+
using (var innerSession = OpenSession())
1106+
{
1107+
var transaction = innerSession.BeginTransaction();
1108+
1109+
Person innerPerson = await (innerSession.CreateQuery(query).UniqueResultAsync<Person>());
1110+
innerPerson.Image = new byte[imageLength];
1111+
innerPerson.Name += namePostFix;
1112+
await (innerSession.UpdateAsync(innerPerson));
1113+
1114+
await (transaction.CommitAsync());
1115+
}
1116+
1117+
// Refreshing the person in the outer session
1118+
await (outerSession.RefreshAsync(outerPerson));
1119+
1120+
Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.True); // Value has changed
1121+
Assert.That(outerPerson.Image.Length, Is.EqualTo(imageLength)); // This is still the old value
1122+
}
1123+
}
10891124

10901125
private static Person GeneratePerson(int i, Person bestFriend)
10911126
{
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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 System.Linq;
12+
using NHibernate.Cfg;
13+
using NHibernate.Cfg.MappingSchema;
14+
using NHibernate.Linq;
15+
using NHibernate.Mapping.ByCode;
16+
using NUnit.Framework;
17+
18+
namespace NHibernate.Test.NHSpecificTest.GH3643
19+
{
20+
using System.Threading.Tasks;
21+
using System.Threading;
22+
[TestFixture]
23+
public class FixtureByCodeAsync : TestCaseMappingByCode
24+
{
25+
protected override void Configure(Configuration configuration)
26+
{
27+
configuration.SetProperty(Environment.UseQueryCache, "true");
28+
configuration.SetProperty(Environment.GenerateStatistics, "true");
29+
}
30+
31+
protected override HbmMapping GetMappings()
32+
{
33+
var mapper = new ModelMapper();
34+
35+
mapper.Class<Entity>(
36+
rc =>
37+
{
38+
rc.Id(x => x.Id);
39+
rc.Bag(
40+
x => x.Children,
41+
m =>
42+
{
43+
m.Access(Accessor.Field);
44+
m.Key(k => k.Column("EntityId"));
45+
m.Cascade(Mapping.ByCode.Cascade.All);
46+
},
47+
r => r.OneToMany());
48+
49+
rc.Cache(
50+
cm =>
51+
{
52+
cm.Include(CacheInclude.All);
53+
cm.Usage(CacheUsage.ReadWrite);
54+
});
55+
});
56+
57+
mapper.Class<ChildEntity>(
58+
rc =>
59+
{
60+
rc.Id(x => x.Id);
61+
rc.Cache(
62+
cm =>
63+
{
64+
cm.Include(CacheInclude.All);
65+
cm.Usage(CacheUsage.ReadWrite);
66+
});
67+
});
68+
69+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
70+
}
71+
72+
protected override void OnSetUp()
73+
{
74+
using var session = OpenSession();
75+
using var transaction = session.BeginTransaction();
76+
77+
var entity = new Entity { Id = EntityId.Id1 };
78+
entity.Children.Add(new ChildEntity { Id = 0 });
79+
entity.Children.Add(new ChildEntity { Id = 1 });
80+
session.Save(entity);
81+
82+
transaction.Commit();
83+
}
84+
85+
protected override void OnTearDown()
86+
{
87+
using var session = OpenSession();
88+
using var transaction = session.BeginTransaction();
89+
90+
session.CreateQuery("delete from ChildEntity").ExecuteUpdate();
91+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
92+
93+
transaction.Commit();
94+
}
95+
96+
[Test]
97+
public async Task LoadsEntityWithEnumIdAndChildrenUsingQueryCacheAsync()
98+
{
99+
await (LoadEntityWithQueryCacheAsync()); // warm up cache
100+
101+
var entity = await (LoadEntityWithQueryCacheAsync());
102+
103+
Assert.That(entity.Children.Count(), Is.EqualTo(2));
104+
105+
Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
106+
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "Unexpected cache put count");
107+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "Unexpected cache hit count");
108+
}
109+
110+
private async Task<Entity> LoadEntityWithQueryCacheAsync(CancellationToken cancellationToken = default(CancellationToken))
111+
{
112+
using var session = OpenSession();
113+
using var transaction = session.BeginTransaction();
114+
var entity = (await (session
115+
.Query<Entity>()
116+
.FetchMany(x => x.Children)
117+
.WithOptions(opt => opt.SetCacheable(true))
118+
.ToListAsync(cancellationToken)))[0];
119+
120+
await (transaction.CommitAsync(cancellationToken));
121+
return entity;
122+
}
123+
}
124+
}

src/NHibernate.Test/FetchLazyProperties/FetchLazyPropertiesFixture.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,41 @@ void AssertPersons(List<Person> results, bool fetched)
10751075
}
10761076
}
10771077
}
1078+
1079+
[Test]
1080+
public void TestRefreshRemovesLazyLoadedProperties()
1081+
{
1082+
using (var outerSession = OpenSession())
1083+
{
1084+
const string query = "from Person fetch Image where Id = 1";
1085+
const string namePostFix = "_MODIFIED";
1086+
const int imageLength = 1985;
1087+
1088+
Person outerPerson = outerSession.CreateQuery(query).UniqueResult<Person>();
1089+
1090+
Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.False); // Normal property
1091+
Assert.That(outerPerson.Image.Length, Is.EqualTo(1)); // Lazy Property
1092+
1093+
// Changing the properties of the person in a different sessions
1094+
using (var innerSession = OpenSession())
1095+
{
1096+
var transaction = innerSession.BeginTransaction();
1097+
1098+
Person innerPerson = innerSession.CreateQuery(query).UniqueResult<Person>();
1099+
innerPerson.Image = new byte[imageLength];
1100+
innerPerson.Name += namePostFix;
1101+
innerSession.Update(innerPerson);
1102+
1103+
transaction.Commit();
1104+
}
1105+
1106+
// Refreshing the person in the outer session
1107+
outerSession.Refresh(outerPerson);
1108+
1109+
Assert.That(outerPerson.Name.EndsWith(namePostFix), Is.True); // Value has changed
1110+
Assert.That(outerPerson.Image.Length, Is.EqualTo(imageLength)); // This is still the old value
1111+
}
1112+
}
10781113

10791114
private static Person GeneratePerson(int i, Person bestFriend)
10801115
{
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Collections.Generic;
2+
3+
// ReSharper disable CollectionNeverUpdated.Local
4+
// ReSharper disable UnassignedGetOnlyAutoProperty
5+
6+
namespace NHibernate.Test.NHSpecificTest.GH3643
7+
{
8+
class Entity
9+
{
10+
private readonly ICollection<ChildEntity> _children = [];
11+
public virtual EntityId Id { get; set; }
12+
public virtual ICollection<ChildEntity> Children => _children;
13+
}
14+
15+
class ChildEntity
16+
{
17+
public virtual int Id { get; set; }
18+
}
19+
20+
enum EntityId
21+
{
22+
Id1,
23+
Id2
24+
}
25+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System.Linq;
2+
using NHibernate.Cfg;
3+
using NHibernate.Cfg.MappingSchema;
4+
using NHibernate.Linq;
5+
using NHibernate.Mapping.ByCode;
6+
using NUnit.Framework;
7+
8+
namespace NHibernate.Test.NHSpecificTest.GH3643
9+
{
10+
[TestFixture]
11+
public class FixtureByCode : TestCaseMappingByCode
12+
{
13+
protected override void Configure(Configuration configuration)
14+
{
15+
configuration.SetProperty(Environment.UseQueryCache, "true");
16+
configuration.SetProperty(Environment.GenerateStatistics, "true");
17+
}
18+
19+
protected override HbmMapping GetMappings()
20+
{
21+
var mapper = new ModelMapper();
22+
23+
mapper.Class<Entity>(
24+
rc =>
25+
{
26+
rc.Id(x => x.Id);
27+
rc.Bag(
28+
x => x.Children,
29+
m =>
30+
{
31+
m.Access(Accessor.Field);
32+
m.Key(k => k.Column("EntityId"));
33+
m.Cascade(Mapping.ByCode.Cascade.All);
34+
},
35+
r => r.OneToMany());
36+
37+
rc.Cache(
38+
cm =>
39+
{
40+
cm.Include(CacheInclude.All);
41+
cm.Usage(CacheUsage.ReadWrite);
42+
});
43+
});
44+
45+
mapper.Class<ChildEntity>(
46+
rc =>
47+
{
48+
rc.Id(x => x.Id);
49+
rc.Cache(
50+
cm =>
51+
{
52+
cm.Include(CacheInclude.All);
53+
cm.Usage(CacheUsage.ReadWrite);
54+
});
55+
});
56+
57+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
58+
}
59+
60+
protected override void OnSetUp()
61+
{
62+
using var session = OpenSession();
63+
using var transaction = session.BeginTransaction();
64+
65+
var entity = new Entity { Id = EntityId.Id1 };
66+
entity.Children.Add(new ChildEntity { Id = 0 });
67+
entity.Children.Add(new ChildEntity { Id = 1 });
68+
session.Save(entity);
69+
70+
transaction.Commit();
71+
}
72+
73+
protected override void OnTearDown()
74+
{
75+
using var session = OpenSession();
76+
using var transaction = session.BeginTransaction();
77+
78+
session.CreateQuery("delete from ChildEntity").ExecuteUpdate();
79+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
80+
81+
transaction.Commit();
82+
}
83+
84+
[Test]
85+
public void LoadsEntityWithEnumIdAndChildrenUsingQueryCache()
86+
{
87+
LoadEntityWithQueryCache(); // warm up cache
88+
89+
var entity = LoadEntityWithQueryCache();
90+
91+
Assert.That(entity.Children.Count(), Is.EqualTo(2));
92+
93+
Assert.That(Sfi.Statistics.QueryExecutionCount, Is.EqualTo(1), "Unexpected execution count");
94+
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(1), "Unexpected cache put count");
95+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "Unexpected cache hit count");
96+
}
97+
98+
private Entity LoadEntityWithQueryCache()
99+
{
100+
using var session = OpenSession();
101+
using var transaction = session.BeginTransaction();
102+
var entity = session
103+
.Query<Entity>()
104+
.FetchMany(x => x.Children)
105+
.WithOptions(opt => opt.SetCacheable(true))
106+
.ToList()[0];
107+
108+
transaction.Commit();
109+
return entity;
110+
}
111+
}
112+
}

src/NHibernate/Async/Event/Default/DefaultRefreshEventListener.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using NHibernate.Cache;
1515
using NHibernate.Engine;
1616
using NHibernate.Impl;
17+
using NHibernate.Intercept;
1718
using NHibernate.Persister.Entity;
1819
using NHibernate.Type;
1920
using NHibernate.Util;
@@ -115,7 +116,9 @@ public virtual async Task OnRefreshAsync(RefreshEvent @event, IDictionary refres
115116
}
116117

117118
await (EvictCachedCollectionsAsync(persister, id, source.Factory, cancellationToken)).ConfigureAwait(false);
118-
119+
120+
RefreshLazyProperties(persister, obj);
121+
119122
// NH Different behavior : NH-1601
120123
// At this point the entity need the real refresh, all elementes of collections are Refreshed,
121124
// the collection state was evicted, but the PersistentCollection (in the entity state)

src/NHibernate/Async/Type/TypeHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ internal static async Task InitializeCollectionsAsync(
125125
continue;
126126
}
127127

128+
value = await (pair.Value.KeyType.AssembleAsync(value, session, null, cancellationToken)).ConfigureAwait(false);
128129
var collection = session.PersistenceContext.GetCollection(new CollectionKey(pair.Value, value));
129130
await (collection.ForceInitializationAsync(cancellationToken)).ConfigureAwait(false);
130131
assembleRow[pair.Key] = collection;

0 commit comments

Comments
 (0)