Skip to content

Commit 960e2e8

Browse files
committed
Ability to select entities in Criteria API projections
1 parent 524d06a commit 960e2e8

File tree

7 files changed

+1262
-0
lines changed

7 files changed

+1262
-0
lines changed
Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
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 NHibernate.Cfg.MappingSchema;
12+
using NHibernate.Criterion;
13+
using NHibernate.Mapping.ByCode;
14+
using NHibernate.SqlCommand;
15+
using NHibernate.Transform;
16+
using NUnit.Framework;
17+
18+
namespace NHibernate.Test.NHSpecificTest.GH948
19+
{
20+
using System.Threading.Tasks;
21+
[TestFixture]
22+
public class ByCodeFixtureAsync : TestCaseMappingByCode
23+
{
24+
private EntityWithCompositeId _entityWithCompositeId;
25+
26+
protected override HbmMapping GetMappings()
27+
{
28+
var mapper = new ModelMapper();
29+
mapper.Class<EntityComplex>(rc =>
30+
{
31+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
32+
33+
rc.Version(ep => ep.Version, vm => { });
34+
35+
rc.Property(x => x.Name);
36+
37+
rc.Property(ep => ep.LazyProp, m => m.Lazy(true));
38+
39+
rc.ManyToOne(ep => ep.Child1, m => m.Column("Child1Id"));
40+
rc.ManyToOne(ep => ep.Child2, m => m.Column("Child2Id"));
41+
rc.ManyToOne(ep => ep.SameTypeChild, m => m.Column("SameTypeChildId"));
42+
43+
rc.Bag(ep => ep.ChildrenList, m =>
44+
{
45+
m.Cascade(Mapping.ByCode.Cascade.All);
46+
m.Inverse(true);
47+
}, a => a.OneToMany());
48+
49+
});
50+
51+
mapper.Class<EntitySimpleChild>(rc =>
52+
{
53+
rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
54+
rc.Property(x => x.Name);
55+
});
56+
57+
mapper.Class<EntityWithCompositeId>(
58+
rc =>
59+
{
60+
rc.ComponentAsId(
61+
e => e.Key,
62+
ekm =>
63+
{
64+
ekm.Property(ek => ek.Id1);
65+
ekm.Property(ek => ek.Id2);
66+
});
67+
68+
rc.Property(e => e.Name);
69+
});
70+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
71+
}
72+
73+
protected override void OnTearDown()
74+
{
75+
using (ISession session = OpenSession())
76+
using (ITransaction transaction = session.BeginTransaction())
77+
{
78+
session.Delete("from System.Object");
79+
80+
session.Flush();
81+
transaction.Commit();
82+
}
83+
}
84+
85+
protected override void OnSetUp()
86+
{
87+
using (var session = OpenSession())
88+
using (var transaction = session.BeginTransaction())
89+
{
90+
var child1 = new EntitySimpleChild
91+
{
92+
Name = "Child1"
93+
};
94+
var child2 = new EntitySimpleChild
95+
{
96+
Name = "Child1"
97+
};
98+
99+
100+
var parent = new EntityComplex
101+
{
102+
Name = "ComplexEnityParent",
103+
Child1 = child1,
104+
Child2 = child2,
105+
LazyProp = "SomeBigValue",
106+
SameTypeChild = new EntityComplex() { Name = "ComplexEntityChild" }
107+
};
108+
109+
_entityWithCompositeId = new EntityWithCompositeId
110+
{
111+
Key = new CompositeKey
112+
{
113+
Id1 = 1,
114+
Id2 = 2
115+
},
116+
Name = "Composite"
117+
};
118+
119+
session.Save(child1);
120+
session.Save(child2);
121+
session.Save(parent.SameTypeChild);
122+
session.Save(parent);
123+
session.Save(_entityWithCompositeId);
124+
125+
session.Flush();
126+
transaction.Commit();
127+
}
128+
}
129+
130+
[Test]
131+
public async Task RootEntityProjectionFullyInitializedAndWithUnfetchedLazyPropertiesByDefaultAsync()
132+
{
133+
using(var sqlLog = new SqlLogSpy())
134+
using (var session = OpenSession())
135+
{
136+
Sfi.Statistics.Clear();
137+
EntityComplex entitypRoot = await (session
138+
.QueryOver<EntityComplex>()
139+
.Where(ec => ec.LazyProp != null)
140+
.Select(Projections.RootEntity())
141+
.Take(1).SingleOrDefaultAsync());
142+
143+
Assert.That(NHibernateUtil.IsInitialized(entitypRoot),Is.True, "Object must be initialized by default");
144+
Assert.That(session.IsReadOnly(entitypRoot),Is.False, "Object must not be readonly by default");
145+
Assert.That(NHibernateUtil.IsPropertyInitialized(entitypRoot, nameof(entitypRoot.LazyProp)), Is.False, "Lazy properties should not be initialized by default.");
146+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
147+
}
148+
}
149+
150+
[Test]
151+
public async Task RootEntityProjectionLazyAsync()
152+
{
153+
using (var session = OpenSession())
154+
{
155+
EntityComplex entitypRoot = await (session
156+
.QueryOver<EntityComplex>()
157+
.Select(Projections.RootEntity().SetLazy(true))
158+
.Take(1).SingleOrDefaultAsync());
159+
160+
161+
Assert.That(NHibernateUtil.IsInitialized(entitypRoot),Is.False, "Object must be lazy loaded.");
162+
}
163+
}
164+
165+
[Test]
166+
public async Task AliasedEntityProjectionAsync()
167+
{
168+
using (var sqlLog = new SqlLogSpy())
169+
using (var session = OpenSession())
170+
{
171+
EntitySimpleChild child1 = null;
172+
child1 = await (session
173+
.QueryOver<EntityComplex>()
174+
.JoinAlias(ep => ep.Child1, () => child1)
175+
.Select(Projections.Entity(() => child1))
176+
.Take(1).SingleOrDefaultAsync<EntitySimpleChild>());
177+
178+
Assert.That(child1, Is.Not.Null);
179+
Assert.That(NHibernateUtil.IsInitialized(child1), Is.True, "Object must be initialized");
180+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
181+
}
182+
}
183+
184+
[Test]
185+
public async Task MultipleLazyEntityProjectionsAsync()
186+
{
187+
using (var session = OpenSession())
188+
{
189+
EntitySimpleChild child1 = null;
190+
EntityComplex root = null;
191+
EntityComplex sameTypeChild = null;
192+
EntitySimpleChild child2 = null;
193+
194+
var result = await (session
195+
.QueryOver(() => root)
196+
.JoinAlias(ep => ep.SameTypeChild, () => sameTypeChild)
197+
.JoinAlias(ep => ep.Child1, () => child1)
198+
.JoinAlias(ep => ep.Child2, () => child2)
199+
.Select(
200+
Projections.RootEntity().SetLazy(true),
201+
Projections.Entity(() => child1).SetLazy(true),
202+
Projections.Entity(() => sameTypeChild).SetLazy(true),
203+
Projections.Entity(() => child2).SetLazy(true)
204+
)
205+
.Take(1).SingleOrDefaultAsync<object[]>());
206+
207+
root = (EntityComplex) result[0];
208+
child1 = (EntitySimpleChild) result[1];
209+
sameTypeChild = (EntityComplex) result[2];
210+
child2 = (EntitySimpleChild) result[3];
211+
212+
Assert.That(NHibernateUtil.IsInitialized(root), Is.False, "Object must be lazy loaded.");
213+
Assert.That(NHibernateUtil.IsInitialized(sameTypeChild), Is.False, "Object must be lazy loaded.");
214+
Assert.That(NHibernateUtil.IsInitialized(child1), Is.False, "Object must be lazy loaded.");
215+
Assert.That(NHibernateUtil.IsInitialized(child2), Is.False, "Object must be lazy loaded.");
216+
217+
//make sure objects are populated from different aliases for the same types
218+
Assert.That(root.Id , Is.Not.EqualTo(sameTypeChild.Id), "Different objects are expected.");
219+
Assert.That(child1.Id , Is.Not.EqualTo(child2.Id), "Different objects are expected.");
220+
221+
}
222+
}
223+
224+
[Test]
225+
public async Task EntityProjectionWithLazyPropertiesFetchedAsync()
226+
{
227+
using (var session = OpenSession())
228+
{
229+
EntityComplex entitypRoot = await (session
230+
.QueryOver<EntityComplex>()
231+
.Where(ec => ec.LazyProp != null)
232+
.Select(Projections.RootEntity().SetAllPropertyFetch(true))
233+
.Take(1).SingleOrDefaultAsync());
234+
235+
Assert.That(entitypRoot, Is.Not.Null);
236+
Assert.That(NHibernateUtil.IsInitialized(entitypRoot), Is.True, "Object must be initialized");
237+
Assert.That(NHibernateUtil.IsPropertyInitialized(entitypRoot, nameof(entitypRoot.LazyProp)), Is.True, "Lazy property must be initialized");
238+
}
239+
}
240+
241+
[Test]
242+
public async Task NullEntityProjectionAsync()
243+
{
244+
using (var session = OpenSession())
245+
{
246+
EntitySimpleChild child1 = null;
247+
child1 = await (session
248+
.QueryOver<EntityComplex>()
249+
.JoinAlias(ep => ep.Child1, () => child1, JoinType.LeftOuterJoin)
250+
.Where(() => child1.Id == null)
251+
.Select(Projections.Entity(() => child1))
252+
.Take(1).SingleOrDefaultAsync<EntitySimpleChild>());
253+
254+
Assert.That(child1, Is.Null);
255+
}
256+
}
257+
258+
[Test]
259+
public async Task MultipleEntitiesProjectionsAsync()
260+
{
261+
using (var sqlLog = new SqlLogSpy())
262+
using (var session = OpenSession())
263+
{
264+
EntityComplex root = null;
265+
EntitySimpleChild child1 = null;
266+
EntitySimpleChild child2 = null;
267+
EntityComplex sameAsRootChild = null;
268+
EntitySimpleChild nullListElem = null;
269+
var objects = await (session
270+
.QueryOver<EntityComplex>()
271+
.JoinAlias(ep => ep.Child1, () => child1)
272+
.JoinAlias(ep => ep.Child2, () => child2)
273+
.JoinAlias(ep => ep.SameTypeChild, () => sameAsRootChild)
274+
.JoinAlias(ep => ep.ChildrenList, () => nullListElem, JoinType.LeftOuterJoin)
275+
.Select(
276+
Projections.RootEntity(),
277+
Projections.Entity(() => child1),
278+
Projections.Entity(() => child2),
279+
Projections.Entity(() => sameAsRootChild),
280+
Projections.Entity(() => nullListElem)
281+
)
282+
.Take(1).SingleOrDefaultAsync<object[]>());
283+
284+
root = (EntityComplex) objects[0];
285+
child1 = (EntitySimpleChild) objects[1];
286+
child2 = (EntitySimpleChild) objects[2];
287+
sameAsRootChild = (EntityComplex) objects[3];
288+
nullListElem = (EntitySimpleChild) objects[4];
289+
290+
Assert.That(NHibernateUtil.IsInitialized(root), Is.True, "Object must be initialized");
291+
Assert.That(NHibernateUtil.IsInitialized(child1), Is.True, "Object must be initialized");
292+
Assert.That(NHibernateUtil.IsInitialized(child2), Is.True, "Object must be initialized");
293+
Assert.That(NHibernateUtil.IsInitialized(sameAsRootChild), Is.True, "Object must be initialized");
294+
Assert.That(nullListElem, Is.Null, "Object must be initialized");
295+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
296+
}
297+
}
298+
299+
class MultipleEntitiesResult
300+
{
301+
public EntityComplex Root { get; set; }
302+
public EntitySimpleChild Child1 { get; set; }
303+
public EntitySimpleChild Child2 { get; set; }
304+
public EntityComplex SameAsRootChild { get; set; }
305+
public EntitySimpleChild NullListElem { get; set; }
306+
}
307+
308+
[Test]
309+
public async Task MultipleEntitiesProjectionsToResultTransformerAsync()
310+
{
311+
using (var sqlLog = new SqlLogSpy())
312+
using (var session = OpenSession())
313+
{
314+
MultipleEntitiesResult result = new MultipleEntitiesResult();
315+
316+
EntityComplex root = null;
317+
EntitySimpleChild child1 = null;
318+
EntitySimpleChild child2 = null;
319+
EntityComplex sameAsRootChild = null;
320+
EntitySimpleChild nullListElem = null;
321+
var r = await (session
322+
.QueryOver<EntityComplex>()
323+
.JoinAlias(ep => ep.Child1, () => child1)
324+
.JoinAlias(ep => ep.Child2, () => child2)
325+
.JoinAlias(ep => ep.SameTypeChild, () => sameAsRootChild)
326+
.JoinAlias(ep => ep.ChildrenList, () => nullListElem, JoinType.LeftOuterJoin)
327+
.Select(
328+
Projections.Alias(Projections.RootEntity(), nameof(result.Root)),
329+
Projections.Entity(() => child1),
330+
Projections.Entity(() => child2),
331+
Projections.Entity(() => sameAsRootChild),
332+
Projections.Entity(() => nullListElem)
333+
)
334+
335+
.TransformUsing(Transformers.AliasToBean<MultipleEntitiesResult>())
336+
.Take(1)
337+
.SingleOrDefaultAsync<MultipleEntitiesResult>());
338+
339+
Assert.That(NHibernateUtil.IsInitialized(r.Root), Is.True, "Object must be initialized");
340+
Assert.That(NHibernateUtil.IsInitialized(r.Child1), Is.True, "Object must be initialized");
341+
Assert.That(NHibernateUtil.IsInitialized(r.Child2), Is.True, "Object must be initialized");
342+
Assert.That(NHibernateUtil.IsInitialized(r.SameAsRootChild), Is.True, "Object must be initialized");
343+
Assert.That(r.NullListElem, Is.Null, "Object must be initialized");
344+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
345+
}
346+
}
347+
348+
349+
[Test]
350+
public async Task ReadOnlyProjectionAsync()
351+
{
352+
using (var session = OpenSession())
353+
{
354+
EntityComplex entitypRoot = await (session
355+
.QueryOver<EntityComplex>()
356+
.Select(Projections.RootEntity().SetReadonly(true))
357+
.Take(1).SingleOrDefaultAsync());
358+
359+
Assert.That(session.IsReadOnly(entitypRoot), Is.True, "Object must be loaded readonly.");
360+
}
361+
}
362+
363+
[Test]
364+
public async Task EntityProjectionForCompositeKeyInitializedAsync()
365+
{
366+
using (var sqlLog = new SqlLogSpy())
367+
using (var session = OpenSession())
368+
{
369+
var composite = await (session
370+
.QueryOver<EntityWithCompositeId>()
371+
.Select(Projections.RootEntity())
372+
.Take(1).SingleOrDefaultAsync());
373+
374+
Assert.That(composite, Is.Not.Null);
375+
Assert.That(NHibernateUtil.IsInitialized(composite), Is.True, "Object must be initialized");
376+
Assert.That(composite, Is.EqualTo(_entityWithCompositeId).Using((EntityWithCompositeId x, EntityWithCompositeId y) => (Equals(x.Key, y.Key) && Equals(x.Name, y.Name)) ? 0 : 1));
377+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1), "Only one SQL select is expected");
378+
}
379+
380+
}
381+
382+
[Test]
383+
public async Task EntityProjectionForCompositeKeyLazyAsync()
384+
{
385+
using (var session = OpenSession())
386+
{
387+
var composite = await (session
388+
.QueryOver<EntityWithCompositeId>()
389+
.Select(Projections.RootEntity().SetLazy(true))
390+
.Take(1).SingleOrDefaultAsync());
391+
392+
Assert.That(composite, Is.Not.Null);
393+
Assert.That(composite, Is.EqualTo(_entityWithCompositeId).Using((EntityWithCompositeId x, EntityWithCompositeId y) => (Equals(x.Key, y.Key)) ? 0 : 1));
394+
Assert.That(NHibernateUtil.IsInitialized(composite), Is.False, "Object must be lazy loaded.");
395+
}
396+
}
397+
}
398+
}

0 commit comments

Comments
 (0)