Skip to content

Commit f46758f

Browse files
authored
Merge pull request #166 from DataObjects-NET/6.0-replace-closure-improvements
Improvement of parameter replacer for compiled queries
2 parents 60177a3 + 235afdb commit f46758f

File tree

2 files changed

+287
-3
lines changed

2 files changed

+287
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
// Copyright (C) 2021 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.Collections.Generic;
7+
using System.Linq;
8+
using System.Text;
9+
using NUnit.Framework;
10+
using Xtensive.Orm.Configuration;
11+
using Xtensive.Orm.Tests.Issues.IssueGithub0164_ClosureReplacementForDescendantTypesModel;
12+
13+
namespace Xtensive.Orm.Tests.Issues.IssueGithub0164_ClosureReplacementForDescendantTypesModel
14+
{
15+
[HierarchyRoot]
16+
public class TestOperation : Entity
17+
{
18+
[Key, Field]
19+
public long Id { get; private set; }
20+
21+
[Field]
22+
public Guid UniqueId { get; set; }
23+
24+
[Field]
25+
[Association(OnOwnerRemove = OnRemoveAction.Clear, OnTargetRemove = OnRemoveAction.Deny, PairTo = "Operation")]
26+
public EntitySet<WorkOrder> WorkOrders { get; private set; }
27+
28+
public string RootClosureByThis()
29+
{
30+
var erpReferences = Session.Query.Execute(q => q.All<WorkOrder>()
31+
.Where(w => w.Operation == this)
32+
.Select(w => w.Str));
33+
34+
return string.Join(" / ", erpReferences.ToArray());
35+
}
36+
37+
public string RootClosureByIdField()
38+
{
39+
var erpReferences = Session.Query.Execute(q => q.All<WorkOrder>()
40+
.Where(w => w.Operation.Id == Id)
41+
.Select(w => w.Str));
42+
43+
return string.Join(" / ", erpReferences.ToArray());
44+
}
45+
46+
public string RootClosureByOtherField()
47+
{
48+
var erpReferences = Session.Query.Execute(q => q.All<WorkOrder>()
49+
.Where(w => w.Operation.UniqueId == UniqueId)
50+
.Select(w => w.Str));
51+
52+
return string.Join(" / ", erpReferences.ToArray());
53+
}
54+
55+
public TestOperation(Session session, WorkOrder wo)
56+
: base(session)
57+
{
58+
UniqueId = Guid.NewGuid();
59+
_ = WorkOrders.Add(wo);
60+
}
61+
}
62+
63+
public class ChildOperation : TestOperation
64+
{
65+
[Field(Length = 10)]
66+
public string SomeField { get; set; }
67+
68+
public string ChildClosureByThis()
69+
{
70+
var erpReferences = Session.Query.Execute(q => q.All<WorkOrder>()
71+
.Where(w => w.Operation == this)
72+
.Select(w => w.Str));
73+
74+
return string.Join(" / ", erpReferences.ToArray());
75+
}
76+
77+
public string ChildClosureByIdField()
78+
{
79+
var erpReferences = Session.Query.Execute(q => q.All<WorkOrder>()
80+
.Where(w => w.Operation.Id == Id)
81+
.Select(w => w.Str));
82+
83+
return string.Join(" / ", erpReferences.ToArray());
84+
}
85+
86+
public string ChildClosureByOtherField()
87+
{
88+
var erpReferences = Session.Query.Execute(q => q.All<WorkOrder>()
89+
.Where(w => w.Operation.UniqueId == UniqueId)
90+
.Select(w => w.Str));
91+
92+
return string.Join(" / ", erpReferences.ToArray());
93+
}
94+
95+
public ChildOperation(Session session, WorkOrder wo)
96+
: base(session, wo)
97+
{
98+
}
99+
}
100+
101+
[HierarchyRoot]
102+
public class WorkOrder : Entity
103+
{
104+
[Key, Field]
105+
public long ID { get; private set; }
106+
107+
[Field]
108+
public TestOperation Operation { get; private set; }
109+
110+
[Field]
111+
public string Str { get; set; }
112+
113+
public WorkOrder(Session session)
114+
: base(session)
115+
{
116+
}
117+
}
118+
}
119+
120+
namespace Xtensive.Orm.Tests.Issues
121+
{
122+
public sealed class IssueGithub0164_ClosureReplacementForDescendantTypes : AutoBuildTest
123+
{
124+
private Guid[] operationIdentifiers;
125+
126+
protected override DomainConfiguration BuildConfiguration()
127+
{
128+
var config = base.BuildConfiguration();
129+
config.Types.Register(typeof(WorkOrder).Assembly, typeof(WorkOrder).Namespace);
130+
return config;
131+
}
132+
133+
protected override void PopulateData()
134+
{
135+
operationIdentifiers = new Guid[6];
136+
137+
using (var session = Domain.OpenSession())
138+
using (var tx = session.OpenTransaction()) {
139+
var wo1 = new WorkOrder(session) { Str = "A" };
140+
var wo2 = new WorkOrder(session) { Str = "B" };
141+
var wo3 = new WorkOrder(session) { Str = "C" };
142+
var wo4 = new WorkOrder(session) { Str = "D" };
143+
var wo5 = new WorkOrder(session) { Str = "E" };
144+
var wo6 = new WorkOrder(session) { Str = "F" };
145+
146+
147+
operationIdentifiers[0] = new TestOperation(session, wo1).UniqueId;
148+
operationIdentifiers[1] = new TestOperation(session, wo2).UniqueId;
149+
operationIdentifiers[2] = new TestOperation(session, wo3).UniqueId;
150+
151+
operationIdentifiers[3] = new ChildOperation(session, wo4).UniqueId;
152+
operationIdentifiers[4] = new ChildOperation(session, wo5).UniqueId;
153+
operationIdentifiers[5] = new ChildOperation(session, wo6).UniqueId;
154+
155+
tx.Complete();
156+
}
157+
}
158+
159+
[Test]
160+
public void ThreeRootSequentialByThis()
161+
{
162+
Domain.QueryCache.Clear();
163+
using (var session = Domain.OpenSession())
164+
using (var tx = session.OpenTransaction()) {
165+
var operation1 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[0]);
166+
var operation2 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[1]);
167+
var operation3 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[2]);
168+
169+
var str1 = operation1.RootClosureByThis();
170+
var str2 = operation2.RootClosureByThis();
171+
var str3 = operation3.RootClosureByThis();
172+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
173+
}
174+
}
175+
176+
[Test]
177+
public void ThreeRootSequentialById()
178+
{
179+
Domain.QueryCache.Clear();
180+
using (var session = Domain.OpenSession())
181+
using (var tx = session.OpenTransaction()) {
182+
var operation1 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[0]);
183+
var operation2 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[1]);
184+
var operation3 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[2]);
185+
186+
var str1 = operation1.RootClosureByIdField();
187+
var str2 = operation2.RootClosureByIdField();
188+
var str3 = operation3.RootClosureByIdField();
189+
190+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
191+
}
192+
}
193+
194+
[Test]
195+
public void ThreeRootSequentialByOtherField()
196+
{
197+
Domain.QueryCache.Clear();
198+
using (var session = Domain.OpenSession())
199+
using (var tx = session.OpenTransaction()) {
200+
var operation1 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[0]);
201+
var operation2 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[1]);
202+
var operation3 = session.Query.All<TestOperation>().First(o => o.UniqueId == operationIdentifiers[2]);
203+
204+
var str1 = operation1.RootClosureByOtherField();
205+
var str2 = operation2.RootClosureByOtherField();
206+
var str3 = operation3.RootClosureByOtherField();
207+
208+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
209+
}
210+
}
211+
212+
[Test]
213+
public void ThreeChildrenSequentialByThis()
214+
{
215+
Domain.QueryCache.Clear();
216+
using (var session = Domain.OpenSession())
217+
using (var tx = session.OpenTransaction()) {
218+
var operation1 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[3]);
219+
var operation2 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[4]);
220+
var operation3 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[5]);
221+
222+
var str1 = operation1.ChildClosureByThis();
223+
var str2 = operation2.ChildClosureByThis();
224+
var str3 = operation3.ChildClosureByThis();
225+
226+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
227+
228+
str1 = operation1.RootClosureByThis();
229+
str2 = operation2.RootClosureByThis();
230+
str3 = operation3.RootClosureByThis();
231+
232+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
233+
}
234+
}
235+
236+
[Test]
237+
public void ThreeChildrenSequentialById()
238+
{
239+
Domain.QueryCache.Clear();
240+
using (var session = Domain.OpenSession())
241+
using (var tx = session.OpenTransaction()) {
242+
var operation1 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[3]);
243+
var operation2 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[4]);
244+
var operation3 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[5]);
245+
246+
var str1 = operation1.ChildClosureByIdField();
247+
var str2 = operation2.ChildClosureByIdField();
248+
var str3 = operation3.ChildClosureByIdField();
249+
250+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
251+
252+
str1 = operation1.RootClosureByIdField();
253+
str2 = operation2.RootClosureByIdField();
254+
str3 = operation3.RootClosureByIdField();
255+
256+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
257+
}
258+
}
259+
260+
[Test]
261+
public void ThreeChildrenSequentialByOtherField()
262+
{
263+
Domain.QueryCache.Clear();
264+
using (var session = Domain.OpenSession())
265+
using (var tx = session.OpenTransaction()) {
266+
var operation1 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[3]);
267+
var operation2 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[4]);
268+
var operation3 = session.Query.All<ChildOperation>().First(o => o.UniqueId == operationIdentifiers[5]);
269+
270+
var str1 = operation1.ChildClosureByOtherField();
271+
var str2 = operation2.ChildClosureByOtherField();
272+
var str3 = operation3.ChildClosureByOtherField();
273+
274+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
275+
276+
str1 = operation1.RootClosureByOtherField();
277+
str2 = operation2.RootClosureByOtherField();
278+
str3 = operation3.RootClosureByOtherField();
279+
280+
Assert.That(str1 != str2 && str2 != str3 && str3 != str1, Is.True);
281+
}
282+
}
283+
}
284+
}

Orm/Xtensive.Orm/Orm/Internals/CompiledQueryRunner.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,13 @@ private void AllocateParameterAndReplacer()
170170
}
171171

172172
if (closureType.DeclaringType == null) {
173-
if (expression.Type == closureType)
173+
if (expression.Type.IsAssignableFrom(closureType))
174174
return Expression.MakeMemberAccess(Expression.Constant(queryParameter, parameterType), valueMemberInfo);
175175
}
176176
else {
177-
if (expression.Type == closureType)
177+
if (expression.Type.IsAssignableFrom(closureType))
178178
return Expression.MakeMemberAccess(Expression.Constant(queryParameter, parameterType), valueMemberInfo);
179-
if (expression.Type == closureType.DeclaringType) {
179+
if (expression.Type.IsAssignableFrom(closureType.DeclaringType)) {
180180
var memberInfo = closureType.TryGetFieldInfoFromClosure(expression.Type);
181181
if (memberInfo != null)
182182
return Expression.MakeMemberAccess(

0 commit comments

Comments
 (0)