Skip to content

Commit f8485f6

Browse files
PleasantDfredericDelaporte
authored andcommitted
Allow Coalesce and Conditional logic on entity properties in LINQ (#1880)
Fixes #1879
1 parent 932ffff commit f8485f6

23 files changed

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

0 commit comments

Comments
 (0)