Skip to content

Commit 5648360

Browse files
committed
Merge branch '6.0' into 6.0-renamefieldhint-validation-bug
# Conflicts: # ChangeLog/6.0.13_dev.txt
2 parents f0c9b6e + 42471b3 commit 5648360

File tree

4 files changed

+243
-30
lines changed

4 files changed

+243
-30
lines changed

ChangeLog/6.0.13_dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
[main] Addressed certain issues of translation connected with comparison with local entity instace within LINQ queries
33
[main] Fixed rare issues of incorrect translation of filtered index expressions including conditional expressions
44
[main] Join/LeftJoin is denied to have the same expression instance for both inner/outer selector
5+
[main] Addressed issue when wrong type of join was chosen when .First/FirstOrDefalult() method was used as subquery
56
[main] Added dedicated exception when RenameFieldHint.TargetType exists in current model but absent in storage model
67
[postgresql] Fixed issue of incorrect translation of contitional expressions including comparison with nullable fields

Orm/Xtensive.Orm.Tests/Issues/IssueJira0743_IncludeDoesNotWorkWithSubqueries.cs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
// Created: 2018.10.18
66

77
using System;
8-
using System.Collections.Generic;
98
using System.Linq;
109
using System.Linq.Expressions;
11-
using System.Text;
1210
using NUnit.Framework;
1311
using Xtensive.Orm.Configuration;
1412
using Xtensive.Orm.Providers;
@@ -345,7 +343,13 @@ public void Case21Test()
345343
var result = session.Query.All<TestEntity>()
346344
.Select(e => values.Contains(e.List.FirstOrDefault().Link.Value3.Value)).ToArray();
347345

348-
Assert.That(result.Single(), Is.True);
346+
var values2 = new MyEnum?[] { MyEnum.Bar, MyEnum.Foo };
347+
var expected = session.Query.All<TestEntity>().AsEnumerable()
348+
.Select(e => values2.Contains(e.List.FirstOrDefault()?.Link.Value3.Value)).ToArray();
349+
350+
Assert.That(result.Length, Is.EqualTo(2));
351+
Assert.That(expected.Length, Is.EqualTo(2));
352+
Assert.That(result.Except(expected).Count(), Is.EqualTo(0));
349353
}
350354
}
351355

@@ -358,7 +362,18 @@ public void Case22Test()
358362
var result = session.Query.All<TestEntity>()
359363
.Select(e => e.List.FirstOrDefault().Link.Value3.Value.In(values)).ToArray();
360364

361-
Assert.That(result.Single(), Is.True);
365+
var expected = session.Query.All<TestEntity>().AsEnumerable()
366+
.Select(e => {
367+
var firsItem = e.List.FirstOrDefault();
368+
if (firsItem == null)
369+
return false;
370+
return firsItem.Link.Value3.Value.In(values);
371+
})
372+
.ToArray();
373+
374+
Assert.That(result.Length, Is.EqualTo(2));
375+
Assert.That(expected.Length, Is.EqualTo(2));
376+
Assert.That(result.Except(expected).Count(), Is.EqualTo(0));
362377
}
363378
}
364379

@@ -397,7 +412,18 @@ public void Case25Test()
397412
var result = session.Query.All<TestEntity>()
398413
.Select(e => values.Contains(e.List.FirstOrDefault().Link.String.Substring(2, 3))).ToArray();
399414

400-
Assert.That(result, Is.All.True);
415+
var expected = session.Query.All<TestEntity>().AsEnumerable()
416+
.Select(e => {
417+
var firsItem = e.List.FirstOrDefault();
418+
if (firsItem == null)
419+
return false;
420+
return values.Contains(firsItem.Link.String.Substring(2, 3));
421+
})
422+
.ToArray();
423+
424+
Assert.That(result.Length, Is.EqualTo(2));
425+
Assert.That(expected.Length, Is.EqualTo(2));
426+
Assert.That(result.Except(expected).Count(), Is.EqualTo(0));
401427
}
402428
}
403429

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// Copyright (C) 2024 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using System.Linq;
7+
using System.Linq.Expressions;
8+
using NUnit.Framework;
9+
using Xtensive.Orm.Configuration;
10+
using Xtensive.Orm.Providers;
11+
using Xtensive.Orm.Tests.Issues.IssueJira0754_FirstOrDefultOnEntitySetCauseIncorrectResultsModel;
12+
13+
14+
namespace Xtensive.Orm.Tests.Issues.IssueJira0754_FirstOrDefultOnEntitySetCauseIncorrectResultsModel
15+
{
16+
[HierarchyRoot]
17+
public class TestEntity : Entity
18+
{
19+
[Key]
20+
[Field(Nullable = false)]
21+
public Guid Id { get; private set; }
22+
23+
[Field]
24+
public string String { get; set; }
25+
26+
[Field]
27+
public ListEntity LinkOnList { get; set; }
28+
29+
[Field]
30+
[Association(PairTo = "Owner")]
31+
public EntitySet<ListEntity> List { get; set; }
32+
33+
[Field]
34+
public TestEntity2 Link { get; set; }
35+
36+
public TestEntity2 VirtualList { get; set; }
37+
38+
public TestEntity2 VirtualLink { get; set; }
39+
40+
public TestEntity(Session session)
41+
: base(session)
42+
{
43+
}
44+
}
45+
46+
[HierarchyRoot]
47+
public class ListEntity : Entity
48+
{
49+
[Key]
50+
[Field(Nullable = false)]
51+
public Guid Id { get; private set; }
52+
53+
[Field]
54+
public string String { get; set; }
55+
56+
[Field(Nullable = false)]
57+
public TestEntity Owner { get; set; }
58+
59+
[Field(Nullable = false)]
60+
public TestEntity2 Link { get; set; }
61+
62+
public ListEntity(Session session)
63+
: base(session)
64+
{
65+
}
66+
}
67+
68+
[HierarchyRoot]
69+
public class TestEntity2 : Entity
70+
{
71+
public TestEntity2(Session session)
72+
: base(session)
73+
{
74+
}
75+
76+
[Key]
77+
[Field(Nullable = false)]
78+
public Guid Id { get; private set; }
79+
80+
[Field(Nullable = false)]
81+
public string String { get; set; }
82+
}
83+
}
84+
85+
86+
namespace Xtensive.Orm.Tests.Issues
87+
{
88+
public class IssueJira0754_FirstOrDefultOnEntitySetCauseIncorrectResults : AutoBuildTest
89+
{
90+
protected override void CheckRequirements() => Require.AllFeaturesSupported(ProviderFeatures.ScalarSubqueries);
91+
92+
protected override DomainConfiguration BuildConfiguration()
93+
{
94+
var config = base.BuildConfiguration();
95+
config.UpgradeMode = DomainUpgradeMode.Recreate;
96+
97+
config.Types.Register(typeof(TestEntity));
98+
config.Types.Register(typeof(TestEntity2));
99+
config.Types.Register(typeof(ListEntity));
100+
101+
Expression<Func<TestEntity, TestEntity2>> exp = e => e.List.FirstOrDefault().Link;
102+
Expression<Func<TestEntity, TestEntity2>> exp2 = e => e.LinkOnList.Link;
103+
config.LinqExtensions.Register(typeof(TestEntity).GetProperty("VirtualList"), exp);
104+
config.LinqExtensions.Register(typeof(TestEntity).GetProperty("VirtualLink"), exp2);
105+
106+
return config;
107+
}
108+
109+
protected override void PopulateData()
110+
{
111+
using (var session = Domain.OpenSession())
112+
using (var tx = session.OpenTransaction()) {
113+
var item = new TestEntity(session) { String = "test" };
114+
var item2 = new TestEntity2(session) { String = "test" };
115+
var list = new ListEntity(session) { String = "test", Owner = item, Link = item2 };
116+
item.LinkOnList = list;
117+
118+
_ = new TestEntity(session) { String = "test2" };
119+
120+
tx.Complete();
121+
}
122+
}
123+
124+
[Test]
125+
public void Case1()
126+
{
127+
using (var session = Domain.OpenSession())
128+
using (var tx = session.OpenTransaction()) {
129+
var count = session.Query.All<TestEntity>()
130+
.Select(e => new { e.VirtualList.String, e.VirtualList.Id })
131+
.Count();
132+
Assert.That(count, Is.EqualTo(2));
133+
134+
var entities = session.Query.All<TestEntity>()
135+
.Select(e => new { e.VirtualList.String, e.VirtualList.Id })
136+
.ToArray().OrderBy(x => x.Id).ToArray();
137+
138+
Assert.That(entities[0].String, Is.Null);
139+
Assert.That(entities[0].Id, Is.EqualTo(Guid.Empty));
140+
141+
Assert.That(entities[1].String, Is.EqualTo("test"));
142+
Assert.That(entities[1].Id, Is.Not.EqualTo(Guid.Empty));
143+
}
144+
}
145+
146+
[Test]
147+
public void Case2()
148+
{
149+
using (var s = Domain.OpenSession())
150+
using (s.Activate())
151+
using (var t = s.OpenTransaction()) {
152+
var count = s.Query.All<TestEntity>()
153+
.Select(e => new { e.String, e.Id })
154+
.Count();
155+
Assert.That(count, Is.EqualTo(2));
156+
157+
var entities = s.Query.All<TestEntity>()
158+
.Select(e => new { e.String, e.Id })
159+
.ToArray().OrderBy(x => x.String).ToArray();
160+
161+
Assert.That(entities[0].String, Is.EqualTo("test"));
162+
Assert.That(entities[0].Id, Is.Not.EqualTo(Guid.Empty));
163+
164+
Assert.That(entities[1].String, Is.EqualTo("test2"));
165+
Assert.That(entities[1].Id, Is.Not.EqualTo(Guid.Empty));
166+
}
167+
}
168+
169+
[Test]
170+
public void Case3()
171+
{
172+
using (var s = Domain.OpenSession())
173+
using (s.Activate())
174+
using (var t = s.OpenTransaction()) {
175+
var count = s.Query.All<TestEntity>()
176+
.Select(e => new { e.LinkOnList.String, e.LinkOnList.Id })
177+
.Count();
178+
Assert.That(count, Is.EqualTo(2));
179+
180+
var entities = s.Query.All<TestEntity>()
181+
.Select(e => new { e.LinkOnList.String, e.LinkOnList.Id })
182+
.ToArray()
183+
.OrderBy(x => x.String)
184+
.ToArray();
185+
186+
Assert.That(entities[0].String, Is.Null);
187+
Assert.That(entities[0].Id, Is.EqualTo(Guid.Empty));
188+
189+
Assert.That(entities[1].String, Is.EqualTo("test"));
190+
Assert.That(entities[1].Id, Is.Not.EqualTo(Guid.Empty));
191+
}
192+
}
193+
}
194+
}

Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2021 Xtensive LLC.
1+
// Copyright (C) 2009-2024 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Alexis Kochetov
@@ -1662,38 +1662,30 @@ private void EnsureEntityReferenceIsJoined(EntityFieldExpression entityFieldExpr
16621662
{
16631663
if (entityFieldExpression.Entity!=null)
16641664
return;
1665-
TypeInfo typeInfo = entityFieldExpression.PersistentType;
1666-
IndexInfo joinedIndex = typeInfo.Indexes.PrimaryIndex;
1665+
var typeInfo = entityFieldExpression.PersistentType;
1666+
var joinedIndex = typeInfo.Indexes.PrimaryIndex;
16671667
var joinedRs = joinedIndex.GetQuery().Alias(context.GetNextAlias());
1668-
Segment<int> keySegment = entityFieldExpression.Mapping;
1669-
Pair<int>[] keyPairs = keySegment.GetItems()
1668+
var keySegment = entityFieldExpression.Mapping;
1669+
var keyPairs = keySegment.GetItems()
16701670
.Select((leftIndex, rightIndex) => new Pair<int>(leftIndex, rightIndex))
16711671
.ToArray();
1672-
ItemProjectorExpression originalItemProjector = entityFieldExpression.OuterParameter==null
1672+
var originalItemProjector = entityFieldExpression.OuterParameter==null
16731673
? context.Bindings[state.Parameters[0]].ItemProjector
16741674
: context.Bindings[entityFieldExpression.OuterParameter].ItemProjector;
1675-
int offset = originalItemProjector.DataSource.Header.Length;
1675+
16761676
var oldDataSource = originalItemProjector.DataSource;
1677-
bool shouldUseLeftJoin = false;
1678-
var filterProvider = oldDataSource as FilterProvider;
1679-
if (filterProvider!=null) {
1680-
var applyProvider = filterProvider.Source as ApplyProvider;
1681-
if (applyProvider!=null)
1682-
shouldUseLeftJoin = applyProvider.ApplyType==JoinType.LeftOuter;
1683-
else {
1684-
var joinProvider = filterProvider.Source as JoinProvider;
1685-
if (joinProvider!=null)
1686-
shouldUseLeftJoin = joinProvider.JoinType==JoinType.LeftOuter;
1687-
}
1688-
}
1689-
else {
1690-
var joinProvider = oldDataSource as JoinProvider;
1691-
if (joinProvider!=null)
1692-
shouldUseLeftJoin = joinProvider.JoinType==JoinType.LeftOuter;
1677+
var offset = oldDataSource.Header.Length;
1678+
var shouldUseLeftJoin = false;
1679+
1680+
var sourceToCheck = (oldDataSource is FilterProvider filterProvider) ? filterProvider.Source : oldDataSource;
1681+
if ((sourceToCheck is ApplyProvider applyProvider && applyProvider.ApplyType == JoinType.LeftOuter) ||
1682+
(sourceToCheck is JoinProvider joinProvider && joinProvider.JoinType == JoinType.LeftOuter)) {
1683+
shouldUseLeftJoin = true;
16931684
}
1685+
16941686
var newDataSource = entityFieldExpression.IsNullable || shouldUseLeftJoin
1695-
? originalItemProjector.DataSource.LeftJoin(joinedRs, keyPairs)
1696-
: originalItemProjector.DataSource.Join(joinedRs, keyPairs);
1687+
? oldDataSource.LeftJoin(joinedRs, keyPairs)
1688+
: oldDataSource.Join(joinedRs, keyPairs);
16971689
originalItemProjector.DataSource = newDataSource;
16981690
entityFieldExpression.RegisterEntityExpression(offset);
16991691
context.RebindApplyParameter(oldDataSource, newDataSource);

0 commit comments

Comments
 (0)