Skip to content

Commit 5e6e99f

Browse files
committed
GH-1879 - Allow Coalesce and Conditional logic on entity properties and collections (LINQ)
1 parent baee90e commit 5e6e99f

14 files changed

+1330
-3
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
using System.Collections.Generic;
2+
using System.Collections.ObjectModel;
3+
using System.Linq;
4+
using System.Linq.Expressions;
5+
using System.Reflection;
6+
using NHibernate.Cfg;
7+
using NHibernate.Hql.Ast;
8+
using NHibernate.Linq.Functions;
9+
using NHibernate.Linq.Visitors;
10+
using NHibernate.Util;
11+
using NUnit.Framework;
12+
13+
namespace NHibernate.Test.NHSpecificTest.GH1879
14+
{
15+
[TestFixture]
16+
public class CoalesceChildThenAccessExtensionMethod : GH1879BaseFixture<Issue>
17+
{
18+
protected override void OnSetUp()
19+
{
20+
using (var session = OpenSession())
21+
using (var transaction = session.BeginTransaction())
22+
{
23+
var clientA = new Client { Name = "Albert" };
24+
var clientB = new Client { Name = "Bob" };
25+
var corpA = new CorporateClient { Name = "Alpha", CorporateId = "1234" };
26+
var corpB = new CorporateClient { Name = "Beta", CorporateId = "5647" };
27+
var clientZ = new Client { Name = null }; // A null value should propagate if the entity is non-null
28+
session.Save(clientA);
29+
session.Save(clientB);
30+
session.Save(corpA);
31+
session.Save(corpB);
32+
session.Save(clientZ);
33+
34+
var projectA = new Project { Name = "A", BillingClient = null, Client = clientA };
35+
var projectB = new Project { Name = "B", BillingClient = corpB, Client = clientA };
36+
var projectC = new Project { Name = "C", BillingClient = null, Client = clientB };
37+
var projectD = new Project { Name = "D", BillingClient = corpA, Client = clientB };
38+
var projectE = new Project { Name = "E", BillingClient = clientZ, Client = clientA };
39+
var projectZ = new Project { Name = "Z", BillingClient = null, Client = null };
40+
session.Save(projectA);
41+
session.Save(projectB);
42+
session.Save(projectC);
43+
session.Save(projectD);
44+
session.Save(projectE);
45+
session.Save(projectZ);
46+
47+
session.Save(new Issue { Name = "01", Project = null, Client = null });
48+
session.Save(new Issue { Name = "02", Project = null, Client = clientA });
49+
session.Save(new Issue { Name = "03", Project = null, Client = clientB });
50+
session.Save(new Issue { Name = "04", Project = projectC, Client = clientA });
51+
session.Save(new Issue { Name = "05", Project = projectA, Client = clientB });
52+
session.Save(new Issue { Name = "06", Project = projectB, Client = clientA });
53+
session.Save(new Issue { Name = "07", Project = projectD, Client = clientB });
54+
session.Save(new Issue { Name = "08", Project = projectZ, Client = corpA });
55+
session.Save(new Issue { Name = "09", Project = projectZ, Client = corpB });
56+
session.Save(new Issue { Name = "10", Project = projectE, Client = clientA });
57+
58+
session.Flush();
59+
transaction.Commit();
60+
}
61+
}
62+
63+
protected override void Configure(Configuration configuration)
64+
{
65+
configuration.LinqToHqlGeneratorsRegistry<TestLinqToHqlGeneratorsRegistry>();
66+
}
67+
68+
private class TestLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
69+
{
70+
public TestLinqToHqlGeneratorsRegistry()
71+
{
72+
this.Merge(new TestHqlGeneratorForMethod());
73+
}
74+
}
75+
76+
private class TestHqlGeneratorForMethod : IHqlGeneratorForMethod
77+
{
78+
/// <inheritdoc />
79+
public IEnumerable<MethodInfo> SupportedMethods => new []
80+
{
81+
ReflectHelper.GetMethodDefinition<Client>(x => x.NameByExtension()),
82+
};
83+
84+
/// <inheritdoc />
85+
public HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
86+
{
87+
return treeBuilder.Dot(visitor.Visit(arguments[0]).AsExpression(), treeBuilder.Ident("Name").AsExpression());
88+
}
89+
}
90+
91+
[Test]
92+
public void WhereClause()
93+
{
94+
AreEqual(
95+
// Actual
96+
q => q.Where(i => (i.Project.BillingClient ?? i.Project.Client ?? i.Client).NameByExtension().StartsWith("A")),
97+
// Expected
98+
q => q.Where(i => (i.Project.BillingClient != null ? i.Project.BillingClient.NameByExtension() : i.Project.Client != null ? i.Project.Client.NameByExtension() : i.Client.NameByExtension()).StartsWith("A"))
99+
);
100+
}
101+
102+
[Test]
103+
public void SelectClause()
104+
{
105+
AreEqual(
106+
// Actual
107+
q => q.OrderBy(i =>i.Name)
108+
.Select(i => (i.Project.BillingClient ?? i.Project.Client ?? i.Client).NameByExtension()),
109+
// Expected
110+
q => q.OrderBy(i =>i.Name)
111+
.Select(i => i.Project.BillingClient != null ? i.Project.BillingClient.NameByExtension() : i.Project.Client != null ? i.Project.Client.NameByExtension() : i.Client.NameByExtension())
112+
);
113+
}
114+
115+
[Test]
116+
public void SelectClauseToAnon()
117+
{
118+
AreEqual(
119+
// Actual
120+
q => q.OrderBy(i =>i.Name)
121+
.Select(i => new { Key =i.Name, Client = (i.Project.BillingClient ?? i.Project.Client ?? i.Client).NameByExtension() }),
122+
// Expected
123+
q => q.OrderBy(i =>i.Name)
124+
.Select(i => new { Key =i.Name, Client = i.Project.BillingClient != null ? i.Project.BillingClient.NameByExtension() : i.Project.Client != null ? i.Project.Client.NameByExtension() : i.Client.NameByExtension() })
125+
);
126+
}
127+
128+
[Test]
129+
public void OrderByClause()
130+
{
131+
AreEqual(
132+
// Actual
133+
q => q.OrderBy(i => (i.Project.BillingClient ?? i.Project.Client ?? i.Client).NameByExtension() ?? "ZZZ")
134+
.ThenBy(i =>i.Name)
135+
.Select(i =>i.Name),
136+
// Expected
137+
q => q.OrderBy(i => (i.Project.BillingClient != null ? i.Project.BillingClient.NameByExtension() : i.Project.Client != null ? i.Project.Client.NameByExtension() : i.Client.NameByExtension()) ?? "ZZZ")
138+
.ThenBy(i =>i.Name)
139+
.Select(i =>i.Name)
140+
);
141+
}
142+
143+
[Test]
144+
public void GroupByClause()
145+
{
146+
AreEqual(
147+
// Actual
148+
q => q.GroupBy(i => (i.Project.BillingClient ?? i.Project.Client ?? i.Client).NameByExtension())
149+
.OrderBy(x => x.Key ?? "ZZZ")
150+
.Select(grp => new { grp.Key, Count = grp.Count() }),
151+
// Expected
152+
q => q.GroupBy(i => i.Project.BillingClient != null ? i.Project.BillingClient.NameByExtension() : i.Project.Client != null ? i.Project.Client.NameByExtension() : i.Client.NameByExtension())
153+
.OrderBy(x => x.Key ?? "ZZZ")
154+
.Select(grp => new { grp.Key, Count = grp.Count() })
155+
);
156+
}
157+
}
158+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System.Linq;
2+
using NUnit.Framework;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH1879
5+
{
6+
[TestFixture]
7+
public class CoalesceChildThenAccessMember : GH1879BaseFixture<Issue>
8+
{
9+
protected override void OnSetUp()
10+
{
11+
using (var session = OpenSession())
12+
using (var transaction = session.BeginTransaction())
13+
{
14+
var clientA = new Client { Name = "Albert" };
15+
var clientB = new Client { Name = "Bob" };
16+
var corpA = new CorporateClient { Name = "Alpha", CorporateId = "1234" };
17+
var corpB = new CorporateClient { Name = "Beta", CorporateId = "5647" };
18+
var clientZ = new Client { Name = null }; // A null value should propagate if the entity is non-null
19+
session.Save(clientA);
20+
session.Save(clientB);
21+
session.Save(corpA);
22+
session.Save(corpB);
23+
session.Save(clientZ);
24+
25+
var projectA = new Project { Name = "A", BillingClient = null, Client = clientA };
26+
var projectB = new Project { Name = "B", BillingClient = corpB, Client = clientA };
27+
var projectC = new Project { Name = "C", BillingClient = null, Client = clientB };
28+
var projectD = new Project { Name = "D", BillingClient = corpA, Client = clientB };
29+
var projectE = new Project { Name = "E", BillingClient = clientZ, Client = clientA };
30+
var projectZ = new Project { Name = "Z", BillingClient = null, Client = null };
31+
session.Save(projectA);
32+
session.Save(projectB);
33+
session.Save(projectC);
34+
session.Save(projectD);
35+
session.Save(projectE);
36+
session.Save(projectZ);
37+
38+
session.Save(new Issue { Name = "01", Project = null, Client = null });
39+
session.Save(new Issue { Name = "02", Project = null, Client = clientA });
40+
session.Save(new Issue { Name = "03", Project = null, Client = clientB });
41+
session.Save(new Issue { Name = "04", Project = projectC, Client = clientA });
42+
session.Save(new Issue { Name = "05", Project = projectA, Client = clientB });
43+
session.Save(new Issue { Name = "06", Project = projectB, Client = clientA });
44+
session.Save(new Issue { Name = "07", Project = projectD, Client = clientB });
45+
session.Save(new Issue { Name = "08", Project = projectZ, Client = corpA });
46+
session.Save(new Issue { Name = "09", Project = projectZ, Client = corpB });
47+
session.Save(new Issue { Name = "10", Project = projectE, Client = clientA });
48+
49+
session.Flush();
50+
transaction.Commit();
51+
}
52+
}
53+
54+
[Test]
55+
public void WhereClause()
56+
{
57+
AreEqual(
58+
// Actual
59+
q => q.Where(i => (i.Project.BillingClient ?? i.Project.Client ?? i.Client).Name.StartsWith("A")),
60+
// Expected
61+
q => q.Where(i => (i.Project.BillingClient != null ? i.Project.BillingClient.Name : i.Project.Client != null ? i.Project.Client.Name : i.Client.Name).StartsWith("A"))
62+
);
63+
}
64+
65+
[Test]
66+
public void SelectClause()
67+
{
68+
AreEqual(
69+
// Actual
70+
q => q.OrderBy(i => i.Name)
71+
.Select(i => (i.Project.BillingClient ?? i.Project.Client ?? i.Client).Name),
72+
// Expected
73+
q => q.OrderBy(i => i.Name)
74+
.Select(i => i.Project.BillingClient != null ? i.Project.BillingClient.Name : i.Project.Client != null ? i.Project.Client.Name : i.Client.Name)
75+
);
76+
}
77+
78+
[Test]
79+
public void SelectClauseToAnon()
80+
{
81+
AreEqual(
82+
// Actual
83+
q => q.OrderBy(i => i.Name)
84+
.Select(i => new { Key = i.Name, Client = (i.Project.BillingClient ?? i.Project.Client ?? i.Client).Name }),
85+
// Expected
86+
q => q.OrderBy(i => i.Name)
87+
.Select(i => new { Key = i.Name, Client = i.Project.BillingClient != null ? i.Project.BillingClient.Name : i.Project.Client != null ? i.Project.Client.Name : i.Client.Name })
88+
);
89+
}
90+
91+
[Test]
92+
public void OrderByClause()
93+
{
94+
AreEqual(
95+
// Actual
96+
q => q.OrderBy(i => (i.Project.BillingClient ?? i.Project.Client ?? i.Client).Name ?? "ZZZ")
97+
.ThenBy(i => i.Name)
98+
.Select(i => i.Name),
99+
// Expected
100+
q => q.OrderBy(i => (i.Project.BillingClient != null ? i.Project.BillingClient.Name : i.Project.Client != null ? i.Project.Client.Name : i.Client.Name) ?? "ZZZ")
101+
.ThenBy(i => i.Name)
102+
.Select(i => i.Name)
103+
);
104+
}
105+
106+
[Test]
107+
public void GroupByClause()
108+
{
109+
AreEqual(
110+
// Actual
111+
q => q.GroupBy(i => (i.Project.BillingClient ?? i.Project.Client ?? i.Client).Name)
112+
.OrderBy(x => x.Key ?? "ZZZ")
113+
.Select(grp => new { grp.Key, Count = grp.Count() }),
114+
// Expected
115+
q => q.GroupBy(i => i.Project.BillingClient != null ? i.Project.BillingClient.Name : i.Project.Client != null ? i.Project.Client.Name : i.Client.Name)
116+
.OrderBy(x => x.Key ?? "ZZZ")
117+
.Select(grp => new { grp.Key, Count = grp.Count() })
118+
);
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)