Skip to content

Commit 871ca25

Browse files
committed
Added custom implemation of PartialEvaluatingExpressionVisitor
1 parent f916c61 commit 871ca25

File tree

4 files changed

+351
-2
lines changed

4 files changed

+351
-2
lines changed

src/NHibernate.Test/Async/Linq/WhereTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,18 @@ public async Task SelectOnCollectionReturnsResultAsync()
844844
Assert.That(result.Children, Is.Not.Empty);
845845
}
846846

847+
[Test(Description = "GH-1556")]
848+
public async Task ContainsOnPersistedCollectionAsync()
849+
{
850+
var animal = await (session.Query<Animal>().SingleAsync(a => a.SerialNumber == "123"));
851+
852+
var result = await (session.Query<Animal>()
853+
.Where(e => animal.Children.Contains(e.Father))
854+
.OrderBy(e => e.Id)
855+
.FirstOrDefaultAsync());
856+
Assert.That(result, Is.Not.Null);
857+
Assert.That(result.SerialNumber, Is.EqualTo("1121"));
858+
}
847859

848860
private static List<object[]> CanUseCompareInQueryDataSource()
849861
{

src/NHibernate.Test/Linq/WhereTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,18 @@ public void SelectOnCollectionReturnsResult()
812812
Assert.That(result.Children, Is.Not.Empty);
813813
}
814814

815+
[Test(Description = "GH-1556")]
816+
public void ContainsOnPersistedCollection()
817+
{
818+
var animal = session.Query<Animal>().Single(a => a.SerialNumber == "123");
819+
820+
var result = session.Query<Animal>()
821+
.Where(e => animal.Children.Contains(e.Father))
822+
.OrderBy(e => e.Id)
823+
.FirstOrDefault();
824+
Assert.That(result, Is.Not.Null);
825+
Assert.That(result.SerialNumber, Is.EqualTo("1121"));
826+
}
815827

816828
private static List<object[]> CanUseCompareInQueryDataSource()
817829
{
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
using System;
2+
using System.Linq;
3+
using System.Linq.Expressions;
4+
using NHibernate.Collection;
5+
using Remotion.Linq.Clauses.Expressions;
6+
using Remotion.Linq.Parsing;
7+
using Remotion.Linq.Parsing.ExpressionVisitors.TreeEvaluation;
8+
9+
namespace NHibernate.Linq.Visitors
10+
{
11+
//Modified version of PartialEvaluatingExpressionVisitor from Relinq (https://github.com/re-motion/Relinq)
12+
//Copyright (c) rubicon IT GmbH, www.rubicon.eu
13+
//
14+
//Used under the Apache Software License 2.0, included at the end of the file
15+
16+
//Modification: Renamed class from PartialEvaluatingExpressionVisitor
17+
internal class CustomPartialEvaluatingExpressionVisitor : RelinqExpressionVisitor
18+
{
19+
public static Expression EvaluateIndependentSubtrees(Expression expression)
20+
{
21+
return EvaluateIndependentSubtrees(expression, new NhEvaluatableExpressionFilter());
22+
}
23+
/// <summary>
24+
/// Takes an expression tree and finds and evaluates all its evaluatable subtrees.
25+
/// </summary>
26+
public static Expression EvaluateIndependentSubtrees(Expression expressionTree, IEvaluatableExpressionFilter evaluatableExpressionFilter)
27+
{
28+
//Modification: Altered argument check syntax
29+
if (expressionTree == null) throw new ArgumentNullException(nameof(expressionTree));
30+
if (evaluatableExpressionFilter == null) throw new ArgumentNullException(nameof(evaluatableExpressionFilter));
31+
32+
var partialEvaluationInfo = EvaluatableTreeFindingExpressionVisitor.Analyze(expressionTree, evaluatableExpressionFilter);
33+
34+
var visitor = new CustomPartialEvaluatingExpressionVisitor(partialEvaluationInfo, evaluatableExpressionFilter);
35+
return visitor.Visit(expressionTree);
36+
}
37+
38+
// _partialEvaluationInfo contains a list of the expressions that are safe to be evaluated.
39+
private readonly PartialEvaluationInfo _partialEvaluationInfo;
40+
private readonly IEvaluatableExpressionFilter _evaluatableExpressionFilter;
41+
42+
private CustomPartialEvaluatingExpressionVisitor(
43+
PartialEvaluationInfo partialEvaluationInfo,
44+
IEvaluatableExpressionFilter evaluatableExpressionFilter)
45+
{
46+
//Modification: Altered argument check syntax
47+
_partialEvaluationInfo = partialEvaluationInfo ?? throw new ArgumentNullException(nameof(partialEvaluationInfo));
48+
_evaluatableExpressionFilter = evaluatableExpressionFilter ?? throw new ArgumentNullException(nameof(evaluatableExpressionFilter));
49+
}
50+
51+
public override Expression Visit(Expression expression)
52+
{
53+
// Only evaluate expressions which do not use any of the surrounding parameter expressions. Don't evaluate
54+
// lambda expressions (even if you could), we want to analyze those later on.
55+
if (expression == null)
56+
return null;
57+
58+
if (expression.NodeType == ExpressionType.Lambda || !_partialEvaluationInfo.IsEvaluatableExpression(expression))
59+
return base.Visit(expression);
60+
61+
Expression evaluatedExpression;
62+
try
63+
{
64+
evaluatedExpression = EvaluateSubtree(expression);
65+
}
66+
catch (Exception ex)
67+
{
68+
// Evaluation caused an exception. Skip evaluation of this expression and proceed as if it weren't evaluable.
69+
var baseVisitedExpression = base.Visit(expression);
70+
// Then wrap the result to capture the exception for the back-end.
71+
return new PartialEvaluationExceptionExpression(ex, baseVisitedExpression);
72+
}
73+
74+
if (evaluatedExpression != expression)
75+
return EvaluateIndependentSubtrees(evaluatedExpression, _evaluatableExpressionFilter);
76+
77+
return evaluatedExpression;
78+
}
79+
80+
/// <summary>
81+
/// Evaluates an evaluatable <see cref="Expression"/> subtree, i.e. an independent expression tree that is compilable and executable
82+
/// without any data being passed in. The result of the evaluation is returned as a <see cref="ConstantExpression"/>; if the subtree
83+
/// is already a <see cref="ConstantExpression"/>, no evaluation is performed.
84+
/// </summary>
85+
/// <param name="subtree">The subtree to be evaluated.</param>
86+
/// <returns>A <see cref="ConstantExpression"/> holding the result of the evaluation.</returns>
87+
private Expression EvaluateSubtree(Expression subtree)
88+
{
89+
//Modification: Altered argument check syntax
90+
if (subtree == null) throw new ArgumentNullException(nameof(subtree));
91+
92+
if (subtree.NodeType == ExpressionType.Constant)
93+
{
94+
var constantExpression = (ConstantExpression) subtree;
95+
var valueAsIQueryable = constantExpression.Value as IQueryable;
96+
//Modification: Added call to IsEvaluatedQueryable
97+
if (valueAsIQueryable != null && !IsEvaluatedQueryable(constantExpression.Value) && valueAsIQueryable.Expression != constantExpression)
98+
return valueAsIQueryable.Expression;
99+
100+
return constantExpression;
101+
}
102+
Expression<Func<object>> lambdaWithoutParameters = Expression.Lambda<Func<object>>(Expression.Convert(subtree, typeof(object)));
103+
var compiledLambda = lambdaWithoutParameters.Compile();
104+
105+
object value = compiledLambda();
106+
return Expression.Constant(value, subtree.Type);
107+
}
108+
109+
//Modification: Added method
110+
protected virtual bool IsEvaluatedQueryable(object queryable)
111+
{
112+
return queryable is IPersistentCollection;
113+
}
114+
115+
}
116+
}
117+
118+
119+
#region license
120+
121+
/*
122+
123+
Apache License
124+
Version 2.0, January 2004
125+
http://www.apache.org/licenses/
126+
127+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
128+
129+
1. Definitions.
130+
131+
"License" shall mean the terms and conditions for use, reproduction,
132+
and distribution as defined by Sections 1 through 9 of this document.
133+
134+
"Licensor" shall mean the copyright owner or entity authorized by
135+
the copyright owner that is granting the License.
136+
137+
"Legal Entity" shall mean the union of the acting entity and all
138+
other entities that control, are controlled by, or are under common
139+
control with that entity. For the purposes of this definition,
140+
"control" means (i) the power, direct or indirect, to cause the
141+
direction or management of such entity, whether by contract or
142+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
143+
outstanding shares, or (iii) beneficial ownership of such entity.
144+
145+
"You" (or "Your") shall mean an individual or Legal Entity
146+
exercising permissions granted by this License.
147+
148+
"Source" form shall mean the preferred form for making modifications,
149+
including but not limited to software source code, documentation
150+
source, and configuration files.
151+
152+
"Object" form shall mean any form resulting from mechanical
153+
transformation or translation of a Source form, including but
154+
not limited to compiled object code, generated documentation,
155+
and conversions to other media types.
156+
157+
"Work" shall mean the work of authorship, whether in Source or
158+
Object form, made available under the License, as indicated by a
159+
copyright notice that is included in or attached to the work
160+
(an example is provided in the Appendix below).
161+
162+
"Derivative Works" shall mean any work, whether in Source or Object
163+
form, that is based on (or derived from) the Work and for which the
164+
editorial revisions, annotations, elaborations, or other modifications
165+
represent, as a whole, an original work of authorship. For the purposes
166+
of this License, Derivative Works shall not include works that remain
167+
separable from, or merely link (or bind by name) to the interfaces of,
168+
the Work and Derivative Works thereof.
169+
170+
"Contribution" shall mean any work of authorship, including
171+
the original version of the Work and any modifications or additions
172+
to that Work or Derivative Works thereof, that is intentionally
173+
submitted to Licensor for inclusion in the Work by the copyright owner
174+
or by an individual or Legal Entity authorized to submit on behalf of
175+
the copyright owner. For the purposes of this definition, "submitted"
176+
means any form of electronic, verbal, or written communication sent
177+
to the Licensor or its representatives, including but not limited to
178+
communication on electronic mailing lists, source code control systems,
179+
and issue tracking systems that are managed by, or on behalf of, the
180+
Licensor for the purpose of discussing and improving the Work, but
181+
excluding communication that is conspicuously marked or otherwise
182+
designated in writing by the copyright owner as "Not a Contribution."
183+
184+
"Contributor" shall mean Licensor and any individual or Legal Entity
185+
on behalf of whom a Contribution has been received by Licensor and
186+
subsequently incorporated within the Work.
187+
188+
2. Grant of Copyright License. Subject to the terms and conditions of
189+
this License, each Contributor hereby grants to You a perpetual,
190+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
191+
copyright license to reproduce, prepare Derivative Works of,
192+
publicly display, publicly perform, sublicense, and distribute the
193+
Work and such Derivative Works in Source or Object form.
194+
195+
3. Grant of Patent License. Subject to the terms and conditions of
196+
this License, each Contributor hereby grants to You a perpetual,
197+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
198+
(except as stated in this section) patent license to make, have made,
199+
use, offer to sell, sell, import, and otherwise transfer the Work,
200+
where such license applies only to those patent claims licensable
201+
by such Contributor that are necessarily infringed by their
202+
Contribution(s) alone or by combination of their Contribution(s)
203+
with the Work to which such Contribution(s) was submitted. If You
204+
institute patent litigation against any entity (including a
205+
cross-claim or counterclaim in a lawsuit) alleging that the Work
206+
or a Contribution incorporated within the Work constitutes direct
207+
or contributory patent infringement, then any patent licenses
208+
granted to You under this License for that Work shall terminate
209+
as of the date such litigation is filed.
210+
211+
4. Redistribution. You may reproduce and distribute copies of the
212+
Work or Derivative Works thereof in any medium, with or without
213+
modifications, and in Source or Object form, provided that You
214+
meet the following conditions:
215+
216+
(a) You must give any other recipients of the Work or
217+
Derivative Works a copy of this License; and
218+
219+
(b) You must cause any modified files to carry prominent notices
220+
stating that You changed the files; and
221+
222+
(c) You must retain, in the Source form of any Derivative Works
223+
that You distribute, all copyright, patent, trademark, and
224+
attribution notices from the Source form of the Work,
225+
excluding those notices that do not pertain to any part of
226+
the Derivative Works; and
227+
228+
(d) If the Work includes a "NOTICE" text file as part of its
229+
distribution, then any Derivative Works that You distribute must
230+
include a readable copy of the attribution notices contained
231+
within such NOTICE file, excluding those notices that do not
232+
pertain to any part of the Derivative Works, in at least one
233+
of the following places: within a NOTICE text file distributed
234+
as part of the Derivative Works; within the Source form or
235+
documentation, if provided along with the Derivative Works; or,
236+
within a display generated by the Derivative Works, if and
237+
wherever such third-party notices normally appear. The contents
238+
of the NOTICE file are for informational purposes only and
239+
do not modify the License. You may add Your own attribution
240+
notices within Derivative Works that You distribute, alongside
241+
or as an addendum to the NOTICE text from the Work, provided
242+
that such additional attribution notices cannot be construed
243+
as modifying the License.
244+
245+
You may add Your own copyright statement to Your modifications and
246+
may provide additional or different license terms and conditions
247+
for use, reproduction, or distribution of Your modifications, or
248+
for any such Derivative Works as a whole, provided Your use,
249+
reproduction, and distribution of the Work otherwise complies with
250+
the conditions stated in this License.
251+
252+
5. Submission of Contributions. Unless You explicitly state otherwise,
253+
any Contribution intentionally submitted for inclusion in the Work
254+
by You to the Licensor shall be under the terms and conditions of
255+
this License, without any additional terms or conditions.
256+
Notwithstanding the above, nothing herein shall supersede or modify
257+
the terms of any separate license agreement you may have executed
258+
with Licensor regarding such Contributions.
259+
260+
6. Trademarks. This License does not grant permission to use the trade
261+
names, trademarks, service marks, or product names of the Licensor,
262+
except as required for reasonable and customary use in describing the
263+
origin of the Work and reproducing the content of the NOTICE file.
264+
265+
7. Disclaimer of Warranty. Unless required by applicable law or
266+
agreed to in writing, Licensor provides the Work (and each
267+
Contributor provides its Contributions) on an "AS IS" BASIS,
268+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
269+
implied, including, without limitation, any warranties or conditions
270+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
271+
PARTICULAR PURPOSE. You are solely responsible for determining the
272+
appropriateness of using or redistributing the Work and assume any
273+
risks associated with Your exercise of permissions under this License.
274+
275+
8. Limitation of Liability. In no event and under no legal theory,
276+
whether in tort (including negligence), contract, or otherwise,
277+
unless required by applicable law (such as deliberate and grossly
278+
negligent acts) or agreed to in writing, shall any Contributor be
279+
liable to You for damages, including any direct, indirect, special,
280+
incidental, or consequential damages of any character arising as a
281+
result of this License or out of the use or inability to use the
282+
Work (including but not limited to damages for loss of goodwill,
283+
work stoppage, computer failure or malfunction, or any and all
284+
other commercial damages or losses), even if such Contributor
285+
has been advised of the possibility of such damages.
286+
287+
9. Accepting Warranty or Additional Liability. While redistributing
288+
the Work or Derivative Works thereof, You may choose to offer,
289+
and charge a fee for, acceptance of support, warranty, indemnity,
290+
or other liability obligations and/or rights consistent with this
291+
License. However, in accepting such obligations, You may act only
292+
on Your own behalf and on Your sole responsibility, not on behalf
293+
of any other Contributor, and only if You agree to indemnify,
294+
defend, and hold each Contributor harmless for any liability
295+
incurred by, or claims asserted against, such Contributor by reason
296+
of your accepting any such warranty or additional liability.
297+
298+
END OF TERMS AND CONDITIONS
299+
300+
APPENDIX: How to apply the Apache License to your work.
301+
302+
To apply the Apache License to your work, attach the following
303+
boilerplate notice, with the fields enclosed by brackets "[]"
304+
replaced with your own identifying information. (Don't include
305+
the brackets!) The text should be enclosed in the appropriate
306+
comment syntax for the file format. We also recommend that a
307+
file or class name and description of purpose be included on the
308+
same "printed page" as the copyright notice for easier
309+
identification within third-party archives.
310+
311+
Copyright [yyyy] [name of copyright owner]
312+
313+
Licensed under the Apache License, Version 2.0 (the "License");
314+
you may not use this file except in compliance with the License.
315+
You may obtain a copy of the License at
316+
317+
http://www.apache.org/licenses/LICENSE-2.0
318+
319+
Unless required by applicable law or agreed to in writing, software
320+
distributed under the License is distributed on an "AS IS" BASIS,
321+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
322+
See the License for the specific language governing permissions and
323+
limitations under the License.
324+
*/
325+
326+
#endregion

src/NHibernate/Linq/Visitors/NhPartialEvaluatingExpressionVisitor.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Linq.Expressions;
44
using Remotion.Linq.Clauses.Expressions;
55
using Remotion.Linq.Parsing;
6-
using Remotion.Linq.Parsing.ExpressionVisitors;
76
using Remotion.Linq.Parsing.ExpressionVisitors.TreeEvaluation;
87

98
namespace NHibernate.Linq.Visitors
@@ -23,7 +22,7 @@ protected override Expression VisitConstant(ConstantExpression expression)
2322

2423
public static Expression EvaluateIndependentSubtrees(Expression expression)
2524
{
26-
var evaluatedExpression = PartialEvaluatingExpressionVisitor.EvaluateIndependentSubtrees(expression, new NhEvaluatableExpressionFilter());
25+
var evaluatedExpression = CustomPartialEvaluatingExpressionVisitor.EvaluateIndependentSubtrees(expression, new NhEvaluatableExpressionFilter());
2726
return new NhPartialEvaluatingExpressionVisitor().Visit(evaluatedExpression);
2827
}
2928

0 commit comments

Comments
 (0)