Skip to content

Commit c4b806c

Browse files
bahusoidhazzik
authored andcommitted
Fix many-to-many filter in hql
Fixes #1994
1 parent 95d78af commit c4b806c

File tree

13 files changed

+393
-70
lines changed

13 files changed

+393
-70
lines changed

src/NHibernate.Test/Async/NHSpecificTest/GH0000/Fixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ protected override void OnTearDown()
4141
// The HQL delete does all the job inside the database without loading the entities, but it does
4242
// not handle delete order for avoiding violating constraints if any. Use
4343
// session.Delete("from System.Object");
44-
// instead if in need of having NHbernate ordering the deletes, but this will cause
44+
// instead if in need of having NHibernate ordering the deletes, but this will cause
4545
// loading the entities in the session.
4646
session.CreateQuery("delete from System.Object").ExecuteUpdate();
4747

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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.Dialect;
13+
using NHibernate.Linq;
14+
using NHibernate.Transform;
15+
using NUnit.Framework;
16+
17+
namespace NHibernate.Test.NHSpecificTest.GH1994
18+
{
19+
using System.Threading.Tasks;
20+
[TestFixture]
21+
public class FixtureAsync : BugTestCase
22+
{
23+
protected override void OnSetUp()
24+
{
25+
using (var session = OpenSession())
26+
using (var transaction = session.BeginTransaction())
27+
{
28+
var a = new Asset();
29+
a.Documents.Add(new Document { IsDeleted = true });
30+
a.Documents.Add(new Document { IsDeleted = false });
31+
32+
session.Save(a);
33+
transaction.Commit();
34+
}
35+
}
36+
37+
protected override void OnTearDown()
38+
{
39+
using (var session = OpenSession())
40+
using (var transaction = session.BeginTransaction())
41+
{
42+
// The HQL delete does all the job inside the database without loading the entities, but it does
43+
// not handle delete order for avoiding violating constraints if any. Use
44+
// session.Delete("from System.Object");
45+
// instead if in need of having NHibernate ordering the deletes, but this will cause
46+
// loading the entities in the session.
47+
48+
session.Delete("from System.Object");
49+
50+
transaction.Commit();
51+
}
52+
}
53+
54+
[Test]
55+
public async Task TestUnfilteredLinqQueryAsync()
56+
{
57+
using (var s = OpenSession())
58+
{
59+
var query = await (s.Query<Asset>()
60+
.FetchMany(x => x.Documents)
61+
.ToListAsync());
62+
63+
Assert.That(query.Count, Is.EqualTo(1), "unfiltered assets");
64+
Assert.That(query[0].Documents.Count, Is.EqualTo(2), "unfiltered asset documents");
65+
}
66+
}
67+
68+
[Test]
69+
public async Task TestFilteredByWhereCollectionLinqQueryAsync()
70+
{
71+
if(Dialect is PostgreSQLDialect)
72+
Assert.Ignore("Dialect doesn't support 0/1 to bool implicit cast");
73+
74+
using (var s = OpenSession())
75+
{
76+
var query = await (s.Query<Asset>()
77+
.FetchMany(x => x.DocumentsFiltered)
78+
.ToListAsync());
79+
80+
Assert.That(query.Count, Is.EqualTo(1), "unfiltered assets");
81+
Assert.That(query[0].DocumentsFiltered.Count, Is.EqualTo(1), "unfiltered asset documents");
82+
}
83+
}
84+
85+
[Test]
86+
public async Task TestFilteredLinqQueryAsync()
87+
{
88+
using (var s = OpenSession())
89+
{
90+
s.EnableFilter("deletedFilter").SetParameter("deletedParam", false);
91+
var query = await (s.Query<Asset>()
92+
.FetchMany(x => x.Documents)
93+
.ToListAsync());
94+
95+
Assert.That(query.Count, Is.EqualTo(1), "filtered assets");
96+
Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents");
97+
}
98+
}
99+
100+
[Test]
101+
public async Task TestFilteredQueryOverAsync()
102+
{
103+
using (var s = OpenSession())
104+
{
105+
s.EnableFilter("deletedFilter").SetParameter("deletedParam", false);
106+
107+
var query = await (s.QueryOver<Asset>()
108+
.Fetch(SelectMode.Fetch, x => x.Documents)
109+
.TransformUsing(Transformers.DistinctRootEntity)
110+
.ListAsync<Asset>());
111+
112+
Assert.That(query.Count, Is.EqualTo(1), "filtered assets");
113+
Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents");
114+
}
115+
}
116+
}
117+
}

src/NHibernate.Test/NHSpecificTest/GH0000/Fixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ protected override void OnTearDown()
2929
// The HQL delete does all the job inside the database without loading the entities, but it does
3030
// not handle delete order for avoiding violating constraints if any. Use
3131
// session.Delete("from System.Object");
32-
// instead if in need of having NHbernate ordering the deletes, but this will cause
32+
// instead if in need of having NHibernate ordering the deletes, but this will cause
3333
// loading the entities in the session.
3434
session.CreateQuery("delete from System.Object").ExecuteUpdate();
3535

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH1994
5+
{
6+
public class Base
7+
{
8+
public virtual Guid Key { get; set; }
9+
10+
public virtual bool IsDeleted { get; set; }
11+
}
12+
13+
public class Asset : Base
14+
{
15+
public virtual ISet<Document> Documents { get; set; } = new HashSet<Document>();
16+
public virtual ISet<Document> DocumentsFiltered { get; set; } = new HashSet<Document>();
17+
}
18+
19+
public class Document : Base
20+
{
21+
public virtual ISet<Asset> Assets { get; set; } = new HashSet<Asset>();
22+
}
23+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
using System.Linq;
2+
using NHibernate.Dialect;
3+
using NHibernate.Linq;
4+
using NHibernate.Transform;
5+
using NUnit.Framework;
6+
7+
namespace NHibernate.Test.NHSpecificTest.GH1994
8+
{
9+
[TestFixture]
10+
public class Fixture : BugTestCase
11+
{
12+
protected override void OnSetUp()
13+
{
14+
using (var session = OpenSession())
15+
using (var transaction = session.BeginTransaction())
16+
{
17+
var a = new Asset();
18+
a.Documents.Add(new Document { IsDeleted = true });
19+
a.Documents.Add(new Document { IsDeleted = false });
20+
21+
session.Save(a);
22+
transaction.Commit();
23+
}
24+
}
25+
26+
protected override void OnTearDown()
27+
{
28+
using (var session = OpenSession())
29+
using (var transaction = session.BeginTransaction())
30+
{
31+
// The HQL delete does all the job inside the database without loading the entities, but it does
32+
// not handle delete order for avoiding violating constraints if any. Use
33+
// session.Delete("from System.Object");
34+
// instead if in need of having NHibernate ordering the deletes, but this will cause
35+
// loading the entities in the session.
36+
37+
session.Delete("from System.Object");
38+
39+
transaction.Commit();
40+
}
41+
}
42+
43+
[Test]
44+
public void TestUnfilteredLinqQuery()
45+
{
46+
using (var s = OpenSession())
47+
{
48+
var query = s.Query<Asset>()
49+
.FetchMany(x => x.Documents)
50+
.ToList();
51+
52+
Assert.That(query.Count, Is.EqualTo(1), "unfiltered assets");
53+
Assert.That(query[0].Documents.Count, Is.EqualTo(2), "unfiltered asset documents");
54+
}
55+
}
56+
57+
[Test]
58+
public void TestFilteredByWhereCollectionLinqQuery()
59+
{
60+
if(Dialect is PostgreSQLDialect)
61+
Assert.Ignore("Dialect doesn't support 0/1 to bool implicit cast");
62+
63+
using (var s = OpenSession())
64+
{
65+
var query = s.Query<Asset>()
66+
.FetchMany(x => x.DocumentsFiltered)
67+
.ToList();
68+
69+
Assert.That(query.Count, Is.EqualTo(1), "unfiltered assets");
70+
Assert.That(query[0].DocumentsFiltered.Count, Is.EqualTo(1), "unfiltered asset documents");
71+
}
72+
}
73+
74+
[Test]
75+
public void TestFilteredLinqQuery()
76+
{
77+
using (var s = OpenSession())
78+
{
79+
s.EnableFilter("deletedFilter").SetParameter("deletedParam", false);
80+
var query = s.Query<Asset>()
81+
.FetchMany(x => x.Documents)
82+
.ToList();
83+
84+
Assert.That(query.Count, Is.EqualTo(1), "filtered assets");
85+
Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents");
86+
}
87+
}
88+
89+
[Test]
90+
public void TestFilteredQueryOver()
91+
{
92+
using (var s = OpenSession())
93+
{
94+
s.EnableFilter("deletedFilter").SetParameter("deletedParam", false);
95+
96+
var query = s.QueryOver<Asset>()
97+
.Fetch(SelectMode.Fetch, x => x.Documents)
98+
.TransformUsing(Transformers.DistinctRootEntity)
99+
.List<Asset>();
100+
101+
Assert.That(query.Count, Is.EqualTo(1), "filtered assets");
102+
Assert.That(query[0].Documents.Count, Is.EqualTo(1), "filtered asset documents");
103+
}
104+
}
105+
}
106+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.GH1994">
4+
5+
<class name="Base" abstract="true" dynamic-update="true" lazy="true">
6+
<id name="Key" column="pdoid" generator="guid.comb"/>
7+
8+
<property name="IsDeleted" column="IsDeleted" />
9+
10+
<filter name="deletedFilter" condition="IsDeleted = :deletedParam"/>
11+
12+
</class>
13+
14+
<union-subclass name="Asset" table="Asset" lazy="true" extends="Base">
15+
<set name="Documents" table="asset_to_document" lazy="true" cascade="all-delete-orphan">
16+
<key column="AssetId"/>
17+
<many-to-many class="Document" column="DocumentId">
18+
<filter name="deletedFilter" condition="IsDeleted = :deletedParam"/>
19+
</many-to-many>
20+
</set>
21+
<set name="DocumentsFiltered" table="asset_to_document" lazy="true" cascade="none">
22+
<key column="AssetId"/>
23+
<many-to-many class="Document" column="DocumentId" where="IsDeleted = 0"/>
24+
</set>
25+
</union-subclass>
26+
27+
<union-subclass name="Document" table="Document" lazy="true" extends="Base">
28+
29+
<set name="Assets" table="asset_to_document" lazy="true" cascade="all-delete-orphan">
30+
<key column="DocumentId"/>
31+
<many-to-many class="Asset" column="AssetId"/>
32+
</set>
33+
</union-subclass>
34+
35+
<filter-def name="deletedFilter">
36+
<filter-param name="deletedParam" type="bool"/>
37+
</filter-def>
38+
39+
</hibernate-mapping>

src/NHibernate/Async/Persister/Collection/AbstractCollectionPersister.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ namespace NHibernate.Persister.Collection
3939
{
4040
using System.Threading.Tasks;
4141
using System.Threading;
42+
4243
public abstract partial class AbstractCollectionPersister : ICollectionMetadata, ISqlLoadableCollection,
4344
IPostInsertIdentityPersister, ISupportSelectModeJoinable, ICompositeKeyPostInsertIdentityPersister
4445
{

src/NHibernate/Loader/BasicLoader.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Generic;
12
using NHibernate.Engine;
23
using NHibernate.Persister.Collection;
34
using NHibernate.Persister.Entity;
@@ -50,7 +51,7 @@ protected override void PostInstantiate()
5051
{
5152
bagCount++;
5253
}
53-
collectionDescriptors[i] = new GeneratedCollectionAliases(collectionPersisters[i], collectionSuffixes[i]);
54+
collectionDescriptors[i] = new GeneratedCollectionAliases(GetCollectionUserProvidedAlias(i), collectionPersisters[i], collectionSuffixes[i]);
5455
}
5556
}
5657
else
@@ -66,6 +67,11 @@ protected override void PostInstantiate()
6667
}
6768
}
6869

70+
protected virtual IDictionary<string, string[]> GetCollectionUserProvidedAlias(int index)
71+
{
72+
return null;
73+
}
74+
6975
private static bool IsBag(ICollectionPersister collectionPersister)
7076
{
7177
var type = collectionPersister.CollectionType.GetType();

0 commit comments

Comments
 (0)