@@ -135,49 +135,72 @@ protected override Expression VisitConstant(ConstantExpression expression)
135135 {
136136 if ( ! _parameters . ContainsKey ( expression ) && ! typeof ( IQueryable ) . IsAssignableFrom ( expression . Type ) && ! IsNullObject ( expression ) )
137137 {
138- // We use null for the type to indicate that the caller should let HQL figure it out.
139- object value = expression . Value ;
140- IType type = null ;
141-
142- // We have a bit more information about the null parameter value.
143- // Figure out a type so that HQL doesn't break on the null. (Related to NH-2430)
144- // In v5.3 types are calculated by ParameterTypeLocator, this logic is only for back compatibility.
145- // TODO 6.0: Remove
146- if ( expression . Value == null )
147- type = NHibernateUtil . GuessType ( expression . Type ) ;
148-
149- // Constant characters should be sent as strings
150- // TODO 6.0: Remove
151- if ( _queryVariables == null && expression . Type == typeof ( char ) )
152- {
153- value = value . ToString ( ) ;
154- }
155-
156- // There is more information available in the Linq expression than to HQL directly.
157- // In some cases it might be advantageous to use the extra info. Assuming this
158- // comes up, it would be nice to combine the HQL parameter type determination code
159- // and the Expression information.
160-
161- NamedParameter parameter = null ;
162- if ( _queryVariables != null &&
163- _queryVariables . TryGetValue ( expression , out var variable ) &&
164- ! _variableParameters . TryGetValue ( variable , out parameter ) )
165- {
166- parameter = CreateParameter ( expression , value , type ) ;
167- _variableParameters . Add ( variable , parameter ) ;
168- }
138+ AddConstantExpressionParameter ( expression , null ) ;
139+ }
169140
170- if ( parameter == null )
171- {
172- parameter = CreateParameter ( expression , value , type ) ;
173- }
141+ return base . VisitConstant ( expression ) ;
142+ }
174143
175- _parameters . Add ( expression , parameter ) ;
144+ protected override Expression VisitUnary ( UnaryExpression node )
145+ {
146+ // If we have an expression like "Convert(<constant>)" we do not want to lose the conversion operation
147+ // because it might be necessary if the types are incompatible with each other, which might happen if
148+ // the expression uses an implicitly or explicitly defined cast operator.
149+ if ( node . NodeType == ExpressionType . Convert &&
150+ node . Method != null && // The implicit/explicit operator method
151+ node . Operand is ConstantExpression constantExpression )
152+ {
153+ // Instead of getting constantExpression.Value, we override the value by compiling and executing this subtree,
154+ // performing the cast.
155+ var lambda = Expression . Lambda < Func < object > > ( Expression . Convert ( node , typeof ( object ) ) ) ;
156+ var compiledLambda = lambda . Compile ( ) ;
176157
177- return base . VisitConstant ( expression ) ;
158+ AddConstantExpressionParameter ( constantExpression , compiledLambda ( ) ) ;
178159 }
179160
180- return base . VisitConstant ( expression ) ;
161+ return base . VisitUnary ( node ) ;
162+ }
163+
164+ private void AddConstantExpressionParameter ( ConstantExpression expression , object overrideValue )
165+ {
166+ // We use null for the type to indicate that the caller should let HQL figure it out.
167+ object value = overrideValue ?? expression . Value ;
168+ IType type = null ;
169+
170+ // We have a bit more information about the null parameter value.
171+ // Figure out a type so that HQL doesn't break on the null. (Related to NH-2430)
172+ // In v5.3 types are calculated by ParameterTypeLocator, this logic is only for back compatibility.
173+ // TODO 6.0: Remove
174+ if ( value == null )
175+ type = NHibernateUtil . GuessType ( expression . Type ) ;
176+
177+ // Constant characters should be sent as strings
178+ // TODO 6.0: Remove
179+ if ( _queryVariables == null && expression . Type == typeof ( char ) )
180+ {
181+ value = value . ToString ( ) ;
182+ }
183+
184+ // There is more information available in the Linq expression than to HQL directly.
185+ // In some cases it might be advantageous to use the extra info. Assuming this
186+ // comes up, it would be nice to combine the HQL parameter type determination code
187+ // and the Expression information.
188+
189+ NamedParameter parameter = null ;
190+ if ( _queryVariables != null &&
191+ _queryVariables . TryGetValue ( expression , out var variable ) &&
192+ ! _variableParameters . TryGetValue ( variable , out parameter ) )
193+ {
194+ parameter = CreateParameter ( expression , value , type ) ;
195+ _variableParameters . Add ( variable , parameter ) ;
196+ }
197+
198+ if ( parameter == null )
199+ {
200+ parameter = CreateParameter ( expression , value , type ) ;
201+ }
202+
203+ _parameters . Add ( expression , parameter ) ;
181204 }
182205
183206 private NamedParameter CreateParameter ( ConstantExpression expression , object value , IType type )
0 commit comments