Skip to content

Commit af48953

Browse files
committed
Pass QueryMode to PreTransform method instead
1 parent aec88f9 commit af48953

File tree

8 files changed

+95
-51
lines changed

8 files changed

+95
-51
lines changed

src/NHibernate.Test/Linq/ConstantTest.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,13 @@ public void ConstantInWhereDoesNotCauseManyKeys()
215215
var q2 = (from c in db.Customers
216216
where c.CustomerId == "ANATR"
217217
select c);
218-
var preTransformResult = NhRelinqQueryParser.PreTransform(q1.Expression, Sfi);
219-
var expression = ExpressionParameterVisitor.Visit(QueryMode.Select, preTransformResult, Sfi, out var parameters1);
218+
var preTransformParameters = new PreTransformationParameters(QueryMode.Select, Sfi);
219+
var preTransformResult = NhRelinqQueryParser.PreTransform(q1.Expression, preTransformParameters);
220+
var expression = ExpressionParameterVisitor.Visit(preTransformResult, out var parameters1);
220221
var k1 = ExpressionKeyVisitor.Visit(expression, parameters1);
221222

222-
var preTransformResult2 = NhRelinqQueryParser.PreTransform(q1.Expression, Sfi);
223-
var expression2 = ExpressionParameterVisitor.Visit(QueryMode.Select, preTransformResult2, Sfi, out var parameters2);
223+
var preTransformResult2 = NhRelinqQueryParser.PreTransform(q1.Expression, preTransformParameters);
224+
var expression2 = ExpressionParameterVisitor.Visit(preTransformResult2, out var parameters2);
224225
var k2 = ExpressionKeyVisitor.Visit(expression2, parameters2);
225226

226227
Assert.That(parameters1, Has.Count.GreaterThan(0), "parameters1");

src/NHibernate.Test/Linq/TryGetMappedTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -773,8 +773,8 @@ private void AssertResult(
773773
expectedComponentType = expectedComponentType ?? (o => o == null);
774774

775775
var expression = query.Expression;
776-
var preTransformResult = NhRelinqQueryParser.PreTransform(expression, Sfi);
777-
expression = ExpressionParameterVisitor.Visit(QueryMode.Select, preTransformResult, Sfi, out var constantToParameterMap);
776+
var preTransformResult = NhRelinqQueryParser.PreTransform(expression, new PreTransformationParameters(QueryMode.Select, Sfi));
777+
expression = ExpressionParameterVisitor.Visit(preTransformResult, out var constantToParameterMap);
778778
var queryModel = NhRelinqQueryParser.Parse(expression);
779779
var requiredHqlParameters = new List<NamedParameterDescriptor>();
780780
var visitorParameters = new VisitorParameters(

src/NHibernate/Linq/NhLinqExpression.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ public NhLinqExpression(Expression expression, ISessionFactoryImplementor sessio
4545
internal NhLinqExpression(QueryMode queryMode, Expression expression, ISessionFactoryImplementor sessionFactory)
4646
{
4747
QueryMode = queryMode;
48-
var preTransformResult = NhRelinqQueryParser.PreTransform(expression, sessionFactory);
48+
var preTransformResult = NhRelinqQueryParser.PreTransform(
49+
expression,
50+
new PreTransformationParameters(queryMode, sessionFactory));
4951
_expression = preTransformResult.Expression;
5052

5153
// We want logging to be as close as possible to the original expression sent from the
@@ -54,11 +56,7 @@ internal NhLinqExpression(QueryMode queryMode, Expression expression, ISessionFa
5456
// referenced from the main query.
5557
LinqLogging.LogExpression("Expression (partially evaluated)", _expression);
5658

57-
_expression = ExpressionParameterVisitor.Visit(
58-
QueryMode,
59-
preTransformResult,
60-
sessionFactory,
61-
out _constantToParameterMap);
59+
_expression = ExpressionParameterVisitor.Visit(preTransformResult, out _constantToParameterMap);
6260

6361
ParameterValuesByName = _constantToParameterMap.Values.Distinct().ToDictionary(p => p.Name,
6462
p => System.Tuple.Create(p.Value, p.Type));

src/NHibernate/Linq/NhRelinqQueryParser.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,28 +54,33 @@ static NhRelinqQueryParser()
5454
/// </summary>
5555
/// <param name="expression">The expression to transform.</param>
5656
/// <returns>The transformed expression.</returns>
57-
[Obsolete("Use overload with an additional sessionFactory parameter")]
57+
[Obsolete("Use overload with PreTransformationParameters parameter")]
5858
public static Expression PreTransform(Expression expression)
5959
{
60-
return PreTransform(expression, null).Expression;
60+
// In order to keep the old behavior use a DML query mode to skip detecting variables,
61+
// which will then generate parameters for each constant expression
62+
return PreTransform(expression, new PreTransformationParameters(QueryMode.Delete, null)).Expression;
6163
}
6264

6365
/// <summary>
6466
/// Applies the minimal transformations required before parametrization,
6567
/// expression key computing and parsing.
6668
/// </summary>
6769
/// <param name="expression">The expression to transform.</param>
68-
/// <param name="sessionFactory">The session factory.</param>
70+
/// <param name="parameters">The parameters used in the transformation process.</param>
6971
/// <returns><see cref="PreTransformationResult"/> that contains the transformed expression.</returns>
70-
public static PreTransformationResult PreTransform(Expression expression, ISessionFactoryImplementor sessionFactory)
72+
public static PreTransformationResult PreTransform(Expression expression, PreTransformationParameters parameters)
7173
{
72-
var queryVariables = new Dictionary<ConstantExpression, QueryVariable>();
74+
parameters.EvaluatableExpressionFilter = new NhEvaluatableExpressionFilter(parameters.SessionFactory);
75+
parameters.QueryVariables = new Dictionary<ConstantExpression, QueryVariable>();
76+
7377
var partiallyEvaluatedExpression = NhPartialEvaluatingExpressionVisitor
74-
.EvaluateIndependentSubtrees(expression, new NhEvaluatableExpressionFilter(sessionFactory), queryVariables);
78+
.EvaluateIndependentSubtrees(expression, parameters);
7579

7680
return new PreTransformationResult(
7781
PreProcessor.Process(partiallyEvaluatedExpression),
78-
queryVariables);
82+
parameters.SessionFactory,
83+
parameters.QueryVariables);
7984
}
8085

8186
public static QueryModel Parse(Expression expression)

src/NHibernate/Linq/Visitors/ExpressionParameterVisitor.cs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ public class ExpressionParameterVisitor : RelinqExpressionVisitor
2020
private readonly Dictionary<QueryVariable, NamedParameter> _variableParameters = new Dictionary<QueryVariable, NamedParameter>();
2121
private readonly IDictionary<ConstantExpression, QueryVariable> _queryVariables;
2222
private readonly ISessionFactoryImplementor _sessionFactory;
23-
private readonly QueryMode _queryMode;
2423

2524
private static readonly MethodInfo QueryableSkipDefinition =
2625
ReflectHelper.FastGetMethodDefinition(Queryable.Skip, default(IQueryable<object>), 0);
@@ -38,21 +37,15 @@ public class ExpressionParameterVisitor : RelinqExpressionVisitor
3837
};
3938

4039
// Since v5.3
41-
[Obsolete("Please use overload with preTransformationResult and queryMode parameters instead.")]
40+
[Obsolete("Please use overload with preTransformationResult parameter instead.")]
4241
public ExpressionParameterVisitor(ISessionFactoryImplementor sessionFactory)
4342
{
4443
_sessionFactory = sessionFactory;
45-
// In order to keep the old behavior use a DML query mode to generate parameters for each constant
46-
_queryMode = QueryMode.Delete;
4744
}
4845

49-
public ExpressionParameterVisitor(
50-
QueryMode queryMode,
51-
ISessionFactoryImplementor sessionFactory,
52-
PreTransformationResult preTransformationResult)
46+
public ExpressionParameterVisitor(PreTransformationResult preTransformationResult)
5347
{
54-
_queryMode = queryMode;
55-
_sessionFactory = sessionFactory;
48+
_sessionFactory = preTransformationResult.SessionFactory;
5649
_queryVariables = preTransformationResult.QueryVariables;
5750
}
5851

@@ -67,12 +60,10 @@ public static IDictionary<ConstantExpression, NamedParameter> Visit(Expression e
6760
}
6861

6962
public static Expression Visit(
70-
QueryMode queryMode,
7163
PreTransformationResult preTransformationResult,
72-
ISessionFactoryImplementor sessionFactory,
7364
out IDictionary<ConstantExpression, NamedParameter> parameters)
7465
{
75-
var visitor = new ExpressionParameterVisitor(queryMode, sessionFactory, preTransformationResult);
66+
var visitor = new ExpressionParameterVisitor(preTransformationResult);
7667
var expression = visitor.Visit(preTransformationResult.Expression);
7768
parameters = visitor._parameters;
7869

@@ -148,11 +139,8 @@ protected override Expression VisitConstant(ConstantExpression expression)
148139
// comes up, it would be nice to combine the HQL parameter type determination code
149140
// and the Expression information.
150141

151-
// When QueryMode.Select, create only one parameter for the same variable. HQL does not support
152-
// reusing parameters for DML queries.
153142
NamedParameter parameter = null;
154-
if (_queryMode == QueryMode.Select &&
155-
_queryVariables != null &&
143+
if (_queryVariables != null &&
156144
_queryVariables.TryGetValue(expression, out var variable) &&
157145
!_variableParameters.TryGetValue(variable, out parameter))
158146
{

src/NHibernate/Linq/Visitors/NhPartialEvaluatingExpressionVisitor.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,26 @@ internal sealed class NhPartialEvaluatingExpressionVisitor : RelinqExpressionVis
3232
/// </summary>
3333
public static Expression EvaluateIndependentSubtrees(
3434
Expression expressionTree,
35-
IEvaluatableExpressionFilter evaluatableExpressionFilter,
36-
IDictionary<ConstantExpression, QueryVariable> queryVariables)
35+
PreTransformationParameters preTransformationParameters)
3736
{
38-
var partialEvaluationInfo = EvaluatableTreeFindingExpressionVisitor.Analyze(expressionTree, evaluatableExpressionFilter);
39-
var visitor = new NhPartialEvaluatingExpressionVisitor(partialEvaluationInfo, evaluatableExpressionFilter, queryVariables);
37+
var partialEvaluationInfo = EvaluatableTreeFindingExpressionVisitor.Analyze(
38+
expressionTree,
39+
preTransformationParameters.EvaluatableExpressionFilter);
40+
var visitor = new NhPartialEvaluatingExpressionVisitor(partialEvaluationInfo, preTransformationParameters);
4041

4142
return visitor.Visit(expressionTree);
4243
}
4344

4445
// _partialEvaluationInfo contains a list of the expressions that are safe to be evaluated.
4546
private readonly PartialEvaluationInfo _partialEvaluationInfo;
46-
private readonly IEvaluatableExpressionFilter _evaluatableExpressionFilter;
47-
private readonly IDictionary<ConstantExpression, QueryVariable> _queryVariables;
47+
private readonly PreTransformationParameters _preTransformationParameters;
4848

4949
private NhPartialEvaluatingExpressionVisitor(
5050
PartialEvaluationInfo partialEvaluationInfo,
51-
IEvaluatableExpressionFilter evaluatableExpressionFilter,
52-
IDictionary<ConstantExpression, QueryVariable> queryVariables)
51+
PreTransformationParameters preTransformationParameters)
5352
{
5453
_partialEvaluationInfo = partialEvaluationInfo;
55-
_evaluatableExpressionFilter = evaluatableExpressionFilter;
56-
_queryVariables = queryVariables;
54+
_preTransformationParameters = preTransformationParameters;
5755
}
5856

5957
public override Expression Visit(Expression expression)
@@ -81,7 +79,7 @@ public override Expression Visit(Expression expression)
8179

8280
if (evaluatedExpression != expression)
8381
{
84-
evaluatedExpression = EvaluateIndependentSubtrees(evaluatedExpression, _evaluatableExpressionFilter, _queryVariables);
82+
evaluatedExpression = EvaluateIndependentSubtrees(evaluatedExpression, _preTransformationParameters);
8583
}
8684

8785
// When having multiple level closure, we have to evaluate each closure independently
@@ -90,13 +88,15 @@ public override Expression Visit(Expression expression)
9088
evaluatedExpression = VisitConstant(constantExpression);
9189
}
9290

93-
// Variables in expressions are never a constant, they are encapsulated as fields of a compiler generated class
91+
// Variables in expressions are never a constant, they are encapsulated as fields of a compiler generated class.
92+
// Skip detecting variables for DML queries as HQL does not support reusing parameters for them.
9493
if (expression.NodeType != ExpressionType.Constant &&
94+
_preTransformationParameters.QueryMode == QueryMode.Select &&
9595
evaluatedExpression is ConstantExpression variableConstant &&
96-
!_queryVariables.ContainsKey(variableConstant) &&
96+
!_preTransformationParameters.QueryVariables.ContainsKey(variableConstant) &&
9797
IsVariable(expression, out var path, out var closureContext))
9898
{
99-
_queryVariables.Add(variableConstant, new QueryVariable(path, closureContext));
99+
_preTransformationParameters.QueryVariables.Add(variableConstant, new QueryVariable(path, closureContext));
100100
}
101101

102102
return evaluatedExpression;
@@ -106,7 +106,7 @@ protected override Expression VisitConstant(ConstantExpression expression)
106106
{
107107
if (expression.Value is Expression value)
108108
{
109-
return EvaluateIndependentSubtrees(value, _evaluatableExpressionFilter, _queryVariables);
109+
return EvaluateIndependentSubtrees(value, _preTransformationParameters);
110110
}
111111

112112
return base.VisitConstant(expression);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Collections.Generic;
2+
using System.Linq.Expressions;
3+
using NHibernate.Engine;
4+
using Remotion.Linq.Parsing.ExpressionVisitors.TreeEvaluation;
5+
6+
namespace NHibernate.Linq.Visitors
7+
{
8+
/// <summary>
9+
/// Contains the information needed by <see cref="NhRelinqQueryParser.PreTransform"/> to perform an early transformation.
10+
/// </summary>
11+
public class PreTransformationParameters
12+
{
13+
/// <summary>
14+
/// The default constructor.
15+
/// </summary>
16+
/// <param name="queryMode">The query mode of the expression to pre-transform.</param>
17+
/// <param name="sessionFactory">The session factory used in the pre-transform process.</param>
18+
public PreTransformationParameters(QueryMode queryMode, ISessionFactoryImplementor sessionFactory)
19+
{
20+
QueryMode = queryMode;
21+
SessionFactory = sessionFactory;
22+
}
23+
24+
/// <summary>
25+
/// The query mode of the expression to pre-transform.
26+
/// </summary>
27+
public QueryMode QueryMode { get; }
28+
29+
/// <summary>
30+
/// The session factory used in the pre-transform process.
31+
/// </summary>
32+
public ISessionFactoryImplementor SessionFactory { get; }
33+
34+
/// <summary>
35+
/// The filter which decides whether a part of the expression will be pre-evalauted or not.
36+
/// </summary>
37+
internal IEvaluatableExpressionFilter EvaluatableExpressionFilter { get; set; }
38+
39+
/// <summary>
40+
/// A dictionary of <see cref="ConstantExpression"/> that were evaluated from variables.
41+
/// </summary>
42+
internal IDictionary<ConstantExpression, QueryVariable> QueryVariables { get; set; }
43+
}
44+
}

src/NHibernate/Linq/Visitors/PreTransformationResult.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq.Expressions;
3+
using NHibernate.Engine;
34

45
namespace NHibernate.Linq.Visitors
56
{
@@ -10,9 +11,11 @@ public class PreTransformationResult
1011
{
1112
internal PreTransformationResult(
1213
Expression expression,
14+
ISessionFactoryImplementor sessionFactory,
1315
IDictionary<ConstantExpression, QueryVariable> queryVariables)
1416
{
1517
Expression = expression;
18+
SessionFactory = sessionFactory;
1619
QueryVariables = queryVariables;
1720
}
1821

@@ -21,6 +24,11 @@ internal PreTransformationResult(
2124
/// </summary>
2225
public Expression Expression { get; }
2326

27+
/// <summary>
28+
/// The session factory used in the pre-transform process.
29+
/// </summary>
30+
public ISessionFactoryImplementor SessionFactory { get; }
31+
2432
/// <summary>
2533
/// A dictionary of <see cref="ConstantExpression"/> that were evaluated from variables.
2634
/// </summary>

0 commit comments

Comments
 (0)