Skip to content

Commit b6d9492

Browse files
illkostinfmaca88
authored andcommitted
Fix caching linq query with ThenFetchMany (nhibernate#2558)
Fixes nhibernate#2559 Co-authored-by: maca88 <[email protected]>
1 parent 7e79102 commit b6d9492

File tree

8 files changed

+381
-0
lines changed

8 files changed

+381
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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.MappingSchema;
13+
using NHibernate.Linq;
14+
using NHibernate.Mapping.ByCode;
15+
using NUnit.Framework;
16+
17+
namespace NHibernate.Test.NHSpecificTest.GH2559
18+
{
19+
using System.Threading.Tasks;
20+
[TestFixture]
21+
public class ByCodeFixtureAsync : TestCaseMappingByCode
22+
{
23+
protected override HbmMapping GetMappings()
24+
{
25+
var mapper = new ModelMapper();
26+
mapper.Class<Person>(rc =>
27+
{
28+
rc.Id(x => x.Id, m => m.Generator(Generators.Guid));
29+
rc.Property(x => x.Name);
30+
rc.Property(x => x.Age);
31+
rc.Set(
32+
x => x.Children,
33+
colMap =>
34+
{
35+
colMap.Inverse(true);
36+
colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
37+
colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
38+
},
39+
rel => rel.OneToMany());
40+
rc.Set(
41+
x => x.Cars,
42+
colMap =>
43+
{
44+
colMap.Inverse(true);
45+
colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
46+
colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
47+
},
48+
rel => rel.OneToMany());
49+
rc.Cache(c => c.Usage(CacheUsage.ReadWrite));
50+
});
51+
mapper.Class<Child>(ch =>
52+
{
53+
ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
54+
ch.Property(x => x.Name);
55+
ch.ManyToOne(c => c.Parent);
56+
57+
ch.Set(
58+
x => x.Pets,
59+
colMap =>
60+
{
61+
colMap.Inverse(true);
62+
colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
63+
colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
64+
},
65+
rel => rel.OneToMany());
66+
67+
ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
68+
});
69+
mapper.Class<Pet>(ch =>
70+
{
71+
ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
72+
ch.Property(x => x.Name);
73+
ch.ManyToOne(c => c.Owner);
74+
75+
ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
76+
});
77+
mapper.Class<Car>(ch =>
78+
{
79+
ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
80+
ch.Property(x => x.Name);
81+
ch.ManyToOne(c => c.Owner);
82+
83+
ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
84+
});
85+
86+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
87+
}
88+
89+
protected override void OnSetUp()
90+
{
91+
using (var session = OpenSession())
92+
using (var transaction = session.BeginTransaction())
93+
{
94+
var person = new Person { Name = "Person 1", Age = 18 };
95+
96+
var car1 = new Car { Name = "Car1", Owner = person };
97+
var car2 = new Car { Name = "Car2", Owner = person };
98+
session.Save(car1);
99+
session.Save(car2);
100+
101+
session.Save(person);
102+
transaction.Commit();
103+
}
104+
}
105+
106+
protected override void OnTearDown()
107+
{
108+
using (var session = OpenSession())
109+
using (var transaction = session.BeginTransaction())
110+
{
111+
session.CreateQuery("delete from Pet").ExecuteUpdate();
112+
session.CreateQuery("delete from Child").ExecuteUpdate();
113+
session.CreateQuery("delete from Car").ExecuteUpdate();
114+
session.CreateQuery("delete from Person").ExecuteUpdate();
115+
transaction.Commit();
116+
}
117+
}
118+
119+
[Test]
120+
public async Task TestQueryCachingWithThenFetchManyAsync()
121+
{
122+
Person dbPerson;
123+
Person cachePerson;
124+
using (var session = OpenSession())
125+
using (var transaction = session.BeginTransaction())
126+
{
127+
var query =
128+
session
129+
.Query<Person>()
130+
.FetchMany(p => p.Children)
131+
.ThenFetchMany(ch => ch.Pets)
132+
.FetchMany(p => p.Cars) as IQueryable<Person>;
133+
134+
query = query.WithOptions(opt =>
135+
opt.SetCacheable(true)
136+
.SetCacheMode(CacheMode.Normal)
137+
.SetCacheRegion("Long_Cache"));
138+
139+
dbPerson = (await (query.ToListAsync())).First(); // First time the result will be cached
140+
cachePerson = (await (query.ToListAsync())).First();
141+
142+
await (transaction.CommitAsync());
143+
}
144+
145+
Assert.That(NHibernateUtil.IsInitialized(dbPerson.Cars), Is.True);
146+
Assert.That(NHibernateUtil.IsInitialized(cachePerson.Cars), Is.True);
147+
Assert.That(dbPerson.Cars, Has.Count.EqualTo(2));
148+
Assert.That(cachePerson.Cars, Has.Count.EqualTo(2));
149+
150+
Assert.That(NHibernateUtil.IsInitialized(dbPerson.Children), Is.True);
151+
Assert.That(NHibernateUtil.IsInitialized(cachePerson.Children), Is.True);
152+
Assert.That(dbPerson.Children, Has.Count.EqualTo(0));
153+
Assert.That(cachePerson.Children, Has.Count.EqualTo(0));
154+
}
155+
}
156+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2559
4+
{
5+
public class Car
6+
{
7+
public virtual Guid Id { get; set; }
8+
public virtual string Name { get; set; }
9+
10+
public virtual Person Owner { get; set; }
11+
}
12+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH2559
5+
{
6+
public class Child
7+
{
8+
public virtual Guid Id { get; set; }
9+
public virtual string Name { get; set; }
10+
11+
public virtual Person Parent { get; set; }
12+
13+
public virtual ISet<Pet> Pets
14+
{
15+
get => _pets ?? (_pets = new HashSet<Pet>());
16+
set => _pets = value;
17+
}
18+
private ISet<Pet> _pets;
19+
}
20+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
using System.Linq;
2+
using NHibernate.Cfg.MappingSchema;
3+
using NHibernate.Linq;
4+
using NHibernate.Mapping.ByCode;
5+
using NUnit.Framework;
6+
7+
namespace NHibernate.Test.NHSpecificTest.GH2559
8+
{
9+
[TestFixture]
10+
public class ByCodeFixture : TestCaseMappingByCode
11+
{
12+
protected override HbmMapping GetMappings()
13+
{
14+
var mapper = new ModelMapper();
15+
mapper.Class<Person>(rc =>
16+
{
17+
rc.Id(x => x.Id, m => m.Generator(Generators.Guid));
18+
rc.Property(x => x.Name);
19+
rc.Property(x => x.Age);
20+
rc.Set(
21+
x => x.Children,
22+
colMap =>
23+
{
24+
colMap.Inverse(true);
25+
colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
26+
colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
27+
},
28+
rel => rel.OneToMany());
29+
rc.Set(
30+
x => x.Cars,
31+
colMap =>
32+
{
33+
colMap.Inverse(true);
34+
colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
35+
colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
36+
},
37+
rel => rel.OneToMany());
38+
rc.Cache(c => c.Usage(CacheUsage.ReadWrite));
39+
});
40+
mapper.Class<Child>(ch =>
41+
{
42+
ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
43+
ch.Property(x => x.Name);
44+
ch.ManyToOne(c => c.Parent);
45+
46+
ch.Set(
47+
x => x.Pets,
48+
colMap =>
49+
{
50+
colMap.Inverse(true);
51+
colMap.Cascade(Mapping.ByCode.Cascade.DeleteOrphans);
52+
colMap.Cache(c => c.Usage(CacheUsage.ReadWrite));
53+
},
54+
rel => rel.OneToMany());
55+
56+
ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
57+
});
58+
mapper.Class<Pet>(ch =>
59+
{
60+
ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
61+
ch.Property(x => x.Name);
62+
ch.ManyToOne(c => c.Owner);
63+
64+
ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
65+
});
66+
mapper.Class<Car>(ch =>
67+
{
68+
ch.Id(x => x.Id, m => m.Generator(Generators.Guid));
69+
ch.Property(x => x.Name);
70+
ch.ManyToOne(c => c.Owner);
71+
72+
ch.Cache(c => c.Usage(CacheUsage.ReadWrite));
73+
});
74+
75+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
76+
}
77+
78+
protected override void OnSetUp()
79+
{
80+
using (var session = OpenSession())
81+
using (var transaction = session.BeginTransaction())
82+
{
83+
var person = new Person { Name = "Person 1", Age = 18 };
84+
85+
var car1 = new Car { Name = "Car1", Owner = person };
86+
var car2 = new Car { Name = "Car2", Owner = person };
87+
session.Save(car1);
88+
session.Save(car2);
89+
90+
session.Save(person);
91+
transaction.Commit();
92+
}
93+
}
94+
95+
protected override void OnTearDown()
96+
{
97+
using (var session = OpenSession())
98+
using (var transaction = session.BeginTransaction())
99+
{
100+
session.CreateQuery("delete from Pet").ExecuteUpdate();
101+
session.CreateQuery("delete from Child").ExecuteUpdate();
102+
session.CreateQuery("delete from Car").ExecuteUpdate();
103+
session.CreateQuery("delete from Person").ExecuteUpdate();
104+
transaction.Commit();
105+
}
106+
}
107+
108+
[Test]
109+
public void TestQueryCachingWithThenFetchMany()
110+
{
111+
Person dbPerson;
112+
Person cachePerson;
113+
using (var session = OpenSession())
114+
using (var transaction = session.BeginTransaction())
115+
{
116+
var query =
117+
session
118+
.Query<Person>()
119+
.FetchMany(p => p.Children)
120+
.ThenFetchMany(ch => ch.Pets)
121+
.FetchMany(p => p.Cars) as IQueryable<Person>;
122+
123+
query = query.WithOptions(opt =>
124+
opt.SetCacheable(true)
125+
.SetCacheMode(CacheMode.Normal)
126+
.SetCacheRegion("Long_Cache"));
127+
128+
dbPerson = query.ToList().First(); // First time the result will be cached
129+
cachePerson = query.ToList().First();
130+
131+
transaction.Commit();
132+
}
133+
134+
Assert.That(NHibernateUtil.IsInitialized(dbPerson.Cars), Is.True);
135+
Assert.That(NHibernateUtil.IsInitialized(cachePerson.Cars), Is.True);
136+
Assert.That(dbPerson.Cars, Has.Count.EqualTo(2));
137+
Assert.That(cachePerson.Cars, Has.Count.EqualTo(2));
138+
139+
Assert.That(NHibernateUtil.IsInitialized(dbPerson.Children), Is.True);
140+
Assert.That(NHibernateUtil.IsInitialized(cachePerson.Children), Is.True);
141+
Assert.That(dbPerson.Children, Has.Count.EqualTo(0));
142+
Assert.That(cachePerson.Children, Has.Count.EqualTo(0));
143+
}
144+
}
145+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH2559
5+
{
6+
public class Person
7+
{
8+
public virtual Guid Id { get; set; }
9+
public virtual string Name { get; set; }
10+
public virtual int Age { get; set; }
11+
12+
public virtual ISet<Car> Cars
13+
{
14+
get => _cars ?? (_cars = new HashSet<Car>());
15+
set => _cars = value;
16+
}
17+
private ISet<Car> _cars;
18+
19+
public virtual ISet<Child> Children
20+
{
21+
get => _children ?? (_children = new HashSet<Child>());
22+
set => _children = value;
23+
}
24+
private ISet<Child> _children;
25+
}
26+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.GH2559
4+
{
5+
public class Pet
6+
{
7+
public virtual Guid Id { get; set; }
8+
public virtual string Name { get; set; }
9+
10+
public virtual Child Owner { get; set; }
11+
}
12+
}

src/NHibernate/Async/Type/CollectionType.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ public override async Task<object> DisassembleAsync(object value, ISessionImplem
104104
public override async Task BeforeAssembleAsync(object oid, ISessionImplementor session, CancellationToken cancellationToken)
105105
{
106106
cancellationToken.ThrowIfCancellationRequested();
107+
if (oid == null)
108+
{
109+
return;
110+
}
111+
107112
var queryCacheQueue = session.PersistenceContext.BatchFetchQueue.QueryCacheQueue;
108113
if (queryCacheQueue == null)
109114
{

0 commit comments

Comments
 (0)