Skip to content

Evaluation failure when using Nullable without a value in LINQ #3156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
krzkowal opened this issue Sep 12, 2022 · 7 comments · Fixed by #3157
Closed

Evaluation failure when using Nullable without a value in LINQ #3156

krzkowal opened this issue Sep 12, 2022 · 7 comments · Fixed by #3157

Comments

@krzkowal
Copy link
Contributor

Using Nullable<T>.HasValue in LINQ throws NHibernate.HibernateException: Evaluation failure on null.HasValue if no value is set.

Test case
[Test]
public void CanUseNullableWithoutAValue()
{
    bool? isActive = null;

    Assert.DoesNotThrow(
        () => _ = session.Query<Role>()
                     .Where(r => !isActive.HasValue || r.IsActive == isActive.Value)
                     .ToList());
}
Error details
NHibernate.HibernateException: Evaluation failure on null.HasValue ---> System.Reflection.TargetException: Non-static method requires a target.
   at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
   at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
   at NHibernate.Impl.ExpressionProcessor.FindValue(Expression expression) in C:\src\nhibernate-core\src\NHibernate\Impl\ExpressionProcessor.cs:line 269
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.EvaluateSubtree(Expression subtree) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 151
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(Expression expression) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 92
   --- End of inner exception stack trace ---
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(Expression expression) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 99
   at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
   at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(Expression expression) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 87
   at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)
   at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(Expression expression) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 87
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(Expression expression) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 87
   at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
   at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(Expression expression) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 87
   at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.Visit(Expression expression) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 87
   at NHibernate.Linq.Visitors.NhPartialEvaluatingExpressionVisitor.EvaluateIndependentSubtrees(Expression expressionTree, PreTransformationParameters preTransformationParameters) in C:\src\nhibernate-core\src\NHibernate\Linq\Visitors\NhPartialEvaluatingExpressionVisitor.cs:line 60
   at NHibernate.Linq.NhRelinqQueryParser.PreTransform(Expression expression, PreTransformationParameters parameters) in C:\src\nhibernate-core\src\NHibernate\Linq\NhRelinqQueryParser.cs:line 70
   at NHibernate.Linq.NhLinqExpression..ctor(QueryMode queryMode, Expression expression, ISessionFactoryImplementor sessionFactory) in C:\src\nhibernate-core\src\NHibernate\Linq\NhLinqExpression.cs:line 50
   at NHibernate.Linq.NhLinqExpression..ctor(Expression expression, ISessionFactoryImplementor sessionFactory) in C:\src\nhibernate-core\src\NHibernate\Linq\NhLinqExpression.cs:line 43
   at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query) in C:\src\nhibernate-core\src\NHibernate\Linq\DefaultQueryProvider.cs:line 204
   at NHibernate.Linq.DefaultQueryProvider.ExecuteList[TResult](Expression expression) in C:\src\nhibernate-core\src\NHibernate\Linq\DefaultQueryProvider.cs:line 107
   at NHibernate.Linq.NhQueryable`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() in C:\src\nhibernate-core\src\NHibernate\Linq\NhQueryable.cs:line 65
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at NHibernate.Test.Linq.WhereTests.<>c__DisplayClass59_0.<CanUseNullableWithoutAValue>b__0() in C:\src\nhibernate-core\src\NHibernate.Test\Linq\WhereTests.cs:line 760
@bahusoid
Copy link
Member

You should always provide NHibernate version (looks like 5.4 dev is used)

As a workaround use null check instead : isActive == null

Just for reference (looks like similar issue): dotnet/coreclr@75d1d39

@oskarb
Copy link
Member

oskarb commented Sep 12, 2022

Does NHibernate recognize the call to the Value property and internally understand that it can't actually call that if the nullable is null? (I don't know if that has been implemented).

Does this work:

.Where(r => isActive == null || r.IsActive == isActive)

@fredericDelaporte
Copy link
Member

fredericDelaporte commented Sep 12, 2022

Another workaround would be to avoid using a condition related only to parameters values in the query. That generally generates queries with better performances. But it also generates more queries, which could be an issue in some circumstances.

var query = session.Query<Role>();
if (isActive.HasValue)
    query = query.Where(r => r.IsActive == isActive);
var results = query.ToList();

Oskar, the error message lets infer the trouble occurs when evaluating null.HasValue, so the issue is with the first condition. Maybe there would be an issue with the next one too, I have not checked. I am not sure the pre-evaluation of expressions which can be pre-evaluated does short-circuit on "lazy" boolean operands. (Or maybe we just ignore .Value calls as not needing to be preevaluated when the resulting value has to be translated to SQL, since SQL does not care about knowing the parameter type is nullable or not.)

I think NHibernate supports HasValue, translating it to null checks, when it is in on members, to be translated to SQL. But here on a parameter, the pre-evaluation fails. We should fix that.

@krzkowal

This comment was marked as resolved.

@bahusoid

This comment was marked as resolved.

@krzkowal

This comment was marked as resolved.

@fredericDelaporte
Copy link
Member

So, actually this bug has not been released. Should we switch it to t:fix in order not to mention a bug which was never released?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants