Skip to content

Commit bfeaaf1

Browse files
authored
NH-3100 - Implement correct Linq semantic for equal and not equal operators considering null behaviours
1 parent 85ca554 commit bfeaaf1

17 files changed

+432
-163
lines changed

src/NHibernate.Test/Linq/FunctionTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,16 @@ where item.IsActive.Equals(true)
331331
ObjectDumper.Write(query);
332332
}
333333

334+
[Test]
335+
public void WhereBoolConditionEquals()
336+
{
337+
var query = from item in db.Role
338+
where item.IsActive.Equals(item.Name != null)
339+
select item;
340+
341+
ObjectDumper.Write(query);
342+
}
343+
334344
[Test]
335345
public void WhereBoolParameterEqual()
336346
{

src/NHibernate.Test/Linq/LinqQuerySamples.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,18 @@ from c in db.Customers
14981498
ObjectDumper.Write(q);
14991499
}
15001500

1501+
[Category("JOIN")]
1502+
[Test(Description = "This sample explictly joins two tables with a composite key and projects results from both tables.")]
1503+
public void DLinqJoin5d()
1504+
{
1505+
var q =
1506+
from c in db.Customers
1507+
join o in db.Orders on new {c.CustomerId, HasContractTitle = c.ContactTitle != null} equals new {o.Customer.CustomerId, HasContractTitle = o.Customer.ContactTitle != null }
1508+
select new { c.ContactName, o.OrderId };
1509+
1510+
ObjectDumper.Write(q);
1511+
}
1512+
15011513
[Category("JOIN")]
15021514
[Test(Description = "This sample explictly joins three tables and projects results from each of them.")]
15031515
public void DLinqJoin6()

src/NHibernate.Test/Linq/NullComparisonTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public void NullEquality()
8484

8585
// Columns against columns
8686
q = from x in session.Query<AnotherEntity>() where x.Input == x.Output select x;
87-
Expect(q, BothSame);
87+
Expect(q, BothSame, BothNull);
8888
}
8989

9090
[Test]
@@ -134,15 +134,15 @@ public void NullInequality()
134134
q = from x in session.Query<AnotherEntity>() where nullVariable != x.Input select x;
135135
ExpectInputIsNotNull(q);
136136
q = from x in session.Query<AnotherEntity>() where notNullVariable != x.Input select x;
137-
Expect(q, BothSame);
137+
Expect(q, BothSame, BothNull, OutputSet);
138138
q = from x in session.Query<AnotherEntity>() where x.Input != nullVariable select x;
139139
ExpectInputIsNotNull(q);
140140
q = from x in session.Query<AnotherEntity>() where x.Input != notNullVariable select x;
141-
Expect(q, BothSame);
141+
Expect(q, BothSame, OutputSet, BothNull);
142142

143143
// Columns against columns
144144
q = from x in session.Query<AnotherEntity>() where x.Input != x.Output select x;
145-
Expect(q, BothDifferent);
145+
Expect(q, BothDifferent, InputSet, OutputSet);
146146
}
147147

148148
[Test]

src/NHibernate.Test/Linq/WhereTests.cs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -379,18 +379,21 @@ public void ProductWithDoubleStringContainsAndNotNull()
379379
var nullAsNullableBool = Expression.Constant(null, typeof(bool?));
380380
var valueProperty = typeof (bool?).GetProperty("Value");
381381

382-
var quantityIsNull = ((Expression<Func<Product, bool>>)(x => x.QuantityPerUnit == null));
383-
var nameIsNull = ((Expression<Func<Product, bool>>)(x => x.Name == null));
382+
Expression<Func<Product, bool>> quantityIsNull = x => x.QuantityPerUnit == null;
383+
Expression<Func<Product, bool>> nameIsNull = x => x.Name == null;
384384

385-
var quantityContains23 = ((Expression<Func<Product, bool?>>)(x => x.QuantityPerUnit.Contains("box")));
386-
var nameContains2 = ((Expression<Func<Product, bool?>>)(x => x.Name.Contains("Cha")));
385+
Expression<Func<Product, bool?>> quantityContains23 = x => x.QuantityPerUnit.Contains("box");
386+
Expression<Func<Product, bool?>> nameContains2 = x => x.Name.Contains("Cha");
387387

388-
var conjunction = Expression.AndAlso(Expression.Condition(quantityIsNull.Body, nullAsNullableBool, quantityContains23.Body),
389-
Expression.Condition(nameIsNull.Body, nullAsNullableBool, nameContains2.Body));
388+
var conjunction = Expression.AndAlso(
389+
Expression.Condition(quantityIsNull.Body, nullAsNullableBool, quantityContains23.Body),
390+
Expression.Condition(nameIsNull.Body, nullAsNullableBool, nameContains2.Body)
391+
);
390392

391-
var condition = Expression.Condition(Expression.Equal(conjunction, Expression.Constant(null)),
392-
Expression.Constant(false),
393-
Expression.MakeMemberAccess(conjunction, valueProperty));
393+
var condition = Expression.Condition(
394+
Expression.Equal(conjunction, Expression.Constant(null)),
395+
Expression.Constant(false),
396+
Expression.MakeMemberAccess(conjunction, valueProperty));
394397

395398
var expr = Expression.Lambda<Func<Product, bool>>(condition, quantityIsNull.Parameters);
396399

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

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public Scenario(ISessionFactory factory)
3636
{
3737
session.Save(new MyClass { Alive = true });
3838
session.Save(new MyClass { Alive = false, MayBeAlive = true });
39+
session.Save(new MyClass { Alive = false, MayBeAlive = false });
3940
session.Flush();
4041
}
4142
}
@@ -51,7 +52,7 @@ public void Dispose()
5152
}
5253

5354
[Test]
54-
public void WhenQueryConstantEqualToMemberThenDoesNotUseTheCaseConstructor()
55+
public void WhenQueryConstantEqualToMemberThenDoNotUseCaseStatement()
5556
{
5657
using (new Scenario(Sfi))
5758
{
@@ -60,7 +61,7 @@ public void WhenQueryConstantEqualToMemberThenDoesNotUseTheCaseConstructor()
6061
using (var sqls = new SqlLogSpy())
6162
{
6263
var list = session.Query<MyClass>().Where(x => x.Alive == false).ToList();
63-
Assert.That(list, Has.Count.EqualTo(1));
64+
Assert.That(list, Has.Count.EqualTo(2));
6465
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
6566
}
6667
using (var sqls = new SqlLogSpy())
@@ -74,28 +75,30 @@ public void WhenQueryConstantEqualToMemberThenDoesNotUseTheCaseConstructor()
7475
}
7576

7677
[Test]
77-
public void WhenQueryConstantNotEqualToMemberThenDoesNotUseTheCaseConstructor()
78+
public void WhenQueryConstantNotEqualToMemberThenDoNotUseCaseStatement()
7879
{
7980
using (new Scenario(Sfi))
8081
{
8182
using (var session = OpenSession())
8283
{
8384
using (var sqls = new SqlLogSpy())
8485
{
85-
Assert.That(session.Query<MyClass>().Where(x => x.Alive != false).ToList(), Has.Count.EqualTo(1));
86+
var list = session.Query<MyClass>().Where(x => x.Alive != false).ToList();
87+
Assert.That(list, Has.Count.EqualTo(1));
8688
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
8789
}
8890
using (var sqls = new SqlLogSpy())
8991
{
90-
Assert.That(session.Query<MyClass>().Where(x => true != x.Alive).ToList(), Has.Count.EqualTo(1));
92+
var list = session.Query<MyClass>().Where(x => true != x.Alive).ToList();
93+
Assert.That(list, Has.Count.EqualTo(2));
9194
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
9295
}
9396
}
9497
}
9598
}
9699

97100
[Test]
98-
public void WhenQueryComplexEqualToComplexThentUseTheCaseConstructorForBoth()
101+
public void WhenQueryComplexEqualToComplexThentUseTheCaseStatementForBoth()
99102
{
100103
using (new Scenario(Sfi))
101104
{
@@ -111,63 +114,69 @@ public void WhenQueryComplexEqualToComplexThentUseTheCaseConstructorForBoth()
111114
}
112115

113116
[Test]
114-
public void WhenQueryConstantEqualToNullableMemberThenUseTheCaseConstructorForMember()
117+
public void WhenQueryConstantEqualToNullableMemberThenDoNotUseCaseStatement()
115118
{
116119
using (new Scenario(Sfi))
117120
{
118121
using (var session = OpenSession())
119122
{
120123
using (var sqls = new SqlLogSpy())
121124
{
122-
Assert.That(session.Query<MyClass>().Where(x => x.MayBeAlive == false).ToList(), Has.Count.EqualTo(1));
123-
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(1));
125+
var list = session.Query<MyClass>().Where(x => x.MayBeAlive == false).ToList();
126+
Assert.That(list, Has.Count.EqualTo(1));
127+
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
124128
}
125129
using (var sqls = new SqlLogSpy())
126130
{
127-
Assert.That(session.Query<MyClass>().Where(x => true == x.MayBeAlive).ToList(), Has.Count.EqualTo(1));
128-
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(1));
131+
var list = session.Query<MyClass>().Where(x => true == x.MayBeAlive).ToList();
132+
Assert.That(list, Has.Count.EqualTo(1));
133+
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
129134
}
130135
}
131136
}
132137
}
133138

134139
[Test]
135-
public void WhenQueryConstantEqualToNullableMemberValueThenDoesNotUseTheCaseConstructorForMember()
140+
public void WhenQueryConstantEqualToNullableMemberValueThenDoNotUseCaseStatement()
136141
{
137142
using (new Scenario(Sfi))
138143
{
139144
using (var session = OpenSession())
140145
{
141146
using (var sqls = new SqlLogSpy())
142147
{
143-
session.Query<MyClass>().Where(x => x.MayBeAlive.Value == false).ToList();
144-
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(1));
148+
var list = session.Query<MyClass>().Where(x => x.MayBeAlive.Value == false).ToList();
149+
Assert.That(list, Has.Count.EqualTo(1));
150+
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
145151
}
146152
using (var sqls = new SqlLogSpy())
147153
{
148-
session.Query<MyClass>().Where(x => true == x.MayBeAlive.Value).ToList();
149-
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(1));
154+
var list = session.Query<MyClass>().Where(x => true == x.MayBeAlive.Value).ToList();
155+
Assert.That(list, Has.Count.EqualTo(1));
156+
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
150157
}
151158
}
152159
}
153160
}
154161

155162
[Test]
156-
public void WhenQueryConstantNotEqualToNullableMemberThenUseTheCaseConstructorForMember()
163+
public void WhenQueryConstantNotEqualToNullableMemberThenDoNotUseCaseStatement()
157164
{
158165
using (new Scenario(Sfi))
159166
{
160167
using (var session = OpenSession())
161168
{
162169
using (var sqls = new SqlLogSpy())
163170
{
164-
Assert.That(session.Query<MyClass>().Where(x => x.MayBeAlive != false).ToList(), Has.Count.EqualTo(1));
165-
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(1));
171+
var list = session.Query<MyClass>().Where(x => x.MayBeAlive != false).ToList();
172+
Assert.That(list, Has.Count.EqualTo(2));
173+
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
166174
}
167175
using (var sqls = new SqlLogSpy())
168176
{
169-
Assert.That(session.Query<MyClass>().Where(x => true != x.MayBeAlive).ToList(), Has.Count.EqualTo(1));
170-
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(1));
177+
var list = session.Query<MyClass>().Where(x => true != x.MayBeAlive).ToList();
178+
Assert.That(list, Has.Count.EqualTo(2));
179+
Assert.That(caseClause.Matches(sqls.GetWholeLog()).Count, Is.EqualTo(0));
171180
}
172181
}
173182
}

0 commit comments

Comments
 (0)