1
1
using System ;
2
- using System . Collections . Generic ;
3
2
using System . Dynamic ;
4
3
using System . Linq ;
5
4
using System . Linq . Expressions ;
6
5
using System . Runtime . CompilerServices ;
7
6
using NHibernate . Engine . Query ;
8
7
using NHibernate . Hql . Ast ;
9
- using NHibernate . Linq . Clauses ;
10
8
using NHibernate . Linq . Expressions ;
11
9
using NHibernate . Linq . Functions ;
12
- using NHibernate . Mapping . ByCode ;
13
10
using NHibernate . Param ;
14
11
using NHibernate . Util ;
15
- using Remotion . Linq . Clauses ;
16
12
using Remotion . Linq . Clauses . Expressions ;
17
- using Remotion . Linq . Clauses . ResultOperators ;
18
13
19
14
namespace NHibernate . Linq . Visitors
20
15
{
@@ -23,17 +18,7 @@ public class HqlGeneratorExpressionVisitor : IHqlExpressionVisitor
23
18
private readonly HqlTreeBuilder _hqlTreeBuilder = new HqlTreeBuilder ( ) ;
24
19
private readonly VisitorParameters _parameters ;
25
20
private readonly ILinqToHqlGeneratorsRegistry _functionRegistry ;
26
- private readonly Dictionary < BinaryExpression , List < MemberExpression > > _equalityNotNullMembers =
27
- new Dictionary < BinaryExpression , List < MemberExpression > > ( ) ;
28
-
29
- private static readonly HashSet < System . Type > NotNullOperators = new HashSet < System . Type > ( )
30
- {
31
- typeof ( AllResultOperator ) ,
32
- typeof ( AnyResultOperator ) ,
33
- typeof ( ContainsResultOperator ) ,
34
- typeof ( CountResultOperator ) ,
35
- typeof ( LongCountResultOperator )
36
- } ;
21
+ private readonly NullableExpressionDetector _nullableExpressionDetector ;
37
22
38
23
public static HqlTreeNode Visit ( Expression expression , VisitorParameters parameters )
39
24
{
@@ -44,6 +29,7 @@ public HqlGeneratorExpressionVisitor(VisitorParameters parameters)
44
29
{
45
30
_functionRegistry = parameters . SessionFactory . Settings . LinqToHqlGeneratorsRegistry ;
46
31
_parameters = parameters ;
32
+ _nullableExpressionDetector = new NullableExpressionDetector ( _parameters . SessionFactory , _functionRegistry ) ;
47
33
}
48
34
49
35
@@ -299,94 +285,6 @@ private HqlTreeNode VisitVBStringComparisonExpression(VBStringComparisonExpressi
299
285
return VisitExpression ( expression . Comparison ) ;
300
286
}
301
287
302
- private void SearchForNotNullMembersCheck ( BinaryExpression expression )
303
- {
304
- // Check for a member not null check that has a not equals expression
305
- // Example: o.Status != null && o.Status != "New"
306
- // Example: (o.Status != null && o.OldStatus != null) && (o.Status != o.OldStatus)
307
- // Example: (o.Status != null && o.OldStatus != null) && (o.Status == o.OldStatus)
308
- if ( expression . NodeType != ExpressionType . AndAlso ||
309
- expression . Right . NodeType != ExpressionType . NotEqual &&
310
- expression . Right . NodeType != ExpressionType . Equal ||
311
- expression . Left . NodeType != ExpressionType . AndAlso &&
312
- expression . Left . NodeType != ExpressionType . NotEqual )
313
- {
314
- return ;
315
- }
316
-
317
- // Skip if there are no member access expressions on the right side
318
- var notEqualExpression = ( BinaryExpression ) expression . Right ;
319
- if ( ! IsMemberAccess ( notEqualExpression . Left ) && ! IsMemberAccess ( notEqualExpression . Right ) )
320
- {
321
- return ;
322
- }
323
-
324
- var notNullMembers = new List < MemberExpression > ( ) ;
325
- // We may have multiple conditions
326
- // Example: o.Status != null && o.OldStatus != null
327
- if ( expression . Left . NodeType == ExpressionType . AndAlso )
328
- {
329
- FindAllNotNullMembers ( ( BinaryExpression ) expression . Left , notNullMembers ) ;
330
- }
331
- else
332
- {
333
- FindNotNullMember ( ( BinaryExpression ) expression . Left , notNullMembers ) ;
334
- }
335
-
336
- if ( notNullMembers . Count > 0 )
337
- {
338
- _equalityNotNullMembers [ notEqualExpression ] = notNullMembers ;
339
- }
340
- }
341
-
342
- private static bool IsMemberAccess ( Expression expression )
343
- {
344
- if ( expression . NodeType == ExpressionType . MemberAccess )
345
- {
346
- return true ;
347
- }
348
-
349
- // Nullable members can be wrapped in a convert expression
350
- return expression is UnaryExpression unaryExpression && unaryExpression . Operand . NodeType == ExpressionType . MemberAccess ;
351
- }
352
-
353
- private static void FindAllNotNullMembers ( BinaryExpression andAlsoExpression , List < MemberExpression > notNullMembers )
354
- {
355
- if ( andAlsoExpression . Right . NodeType == ExpressionType . NotEqual )
356
- {
357
- FindNotNullMember ( ( BinaryExpression ) andAlsoExpression . Right , notNullMembers ) ;
358
- }
359
- else if ( andAlsoExpression . Right . NodeType == ExpressionType . AndAlso )
360
- {
361
- FindAllNotNullMembers ( ( BinaryExpression ) andAlsoExpression . Right , notNullMembers ) ;
362
- }
363
- else
364
- {
365
- return ;
366
- }
367
-
368
- if ( andAlsoExpression . Left . NodeType == ExpressionType . NotEqual )
369
- {
370
- FindNotNullMember ( ( BinaryExpression ) andAlsoExpression . Left , notNullMembers ) ;
371
- }
372
- else if ( andAlsoExpression . Left . NodeType == ExpressionType . AndAlso )
373
- {
374
- FindAllNotNullMembers ( ( BinaryExpression ) andAlsoExpression . Left , notNullMembers ) ;
375
- }
376
- }
377
-
378
- private static void FindNotNullMember ( BinaryExpression notEqualExpression , List < MemberExpression > notNullMembers )
379
- {
380
- if ( notEqualExpression . Left . NodeType == ExpressionType . MemberAccess && VisitorUtil . IsNullConstant ( notEqualExpression . Right ) )
381
- {
382
- notNullMembers . Add ( ( MemberExpression ) notEqualExpression . Left ) ;
383
- }
384
- else if ( VisitorUtil . IsNullConstant ( notEqualExpression . Left ) && notEqualExpression . Right . NodeType == ExpressionType . MemberAccess )
385
- {
386
- notNullMembers . Add ( ( MemberExpression ) notEqualExpression . Right ) ;
387
- }
388
- }
389
-
390
288
protected HqlTreeNode VisitBinaryExpression ( BinaryExpression expression )
391
289
{
392
290
if ( expression . NodeType == ExpressionType . Equal )
@@ -398,7 +296,7 @@ protected HqlTreeNode VisitBinaryExpression(BinaryExpression expression)
398
296
return TranslateInequalityComparison ( expression ) ;
399
297
}
400
298
401
- SearchForNotNullMembersCheck ( expression ) ;
299
+ _nullableExpressionDetector . SearchForNotNullMemberChecks ( expression ) ;
402
300
403
301
var lhs = VisitExpression ( expression . Left ) . AsExpression ( ) ;
404
302
var rhs = VisitExpression ( expression . Right ) . AsExpression ( ) ;
@@ -481,8 +379,8 @@ private HqlTreeNode TranslateInequalityComparison(BinaryExpression expression)
481
379
return _hqlTreeBuilder . IsNotNull ( lhs ) ;
482
380
}
483
381
484
- var lhsNullable = IsNullable ( expression . Left , expression ) ;
485
- var rhsNullable = IsNullable ( expression . Right , expression ) ;
382
+ var lhsNullable = _nullableExpressionDetector . IsNullable ( expression . Left , expression ) ;
383
+ var rhsNullable = _nullableExpressionDetector . IsNullable ( expression . Right , expression ) ;
486
384
487
385
var inequality = _hqlTreeBuilder . Inequality ( lhs , rhs ) ;
488
386
@@ -544,8 +442,8 @@ private HqlTreeNode TranslateEqualityComparison(BinaryExpression expression)
544
442
return _hqlTreeBuilder . IsNull ( ( lhs ) ) ;
545
443
}
546
444
547
- var lhsNullable = IsNullable ( expression . Left , expression ) ;
548
- var rhsNullable = IsNullable ( expression . Right , expression ) ;
445
+ var lhsNullable = _nullableExpressionDetector . IsNullable ( expression . Left , expression ) ;
446
+ var rhsNullable = _nullableExpressionDetector . IsNullable ( expression . Right , expression ) ;
549
447
550
448
var equality = _hqlTreeBuilder . Equality ( lhs , rhs ) ;
551
449
@@ -564,190 +462,6 @@ private HqlTreeNode TranslateEqualityComparison(BinaryExpression expression)
564
462
_hqlTreeBuilder . IsNull ( rhs2 ) ) ) ;
565
463
}
566
464
567
- private bool IsNullable ( Expression expression , BinaryExpression equalityExpression )
568
- {
569
- var currentExpression = expression ;
570
- while ( true )
571
- {
572
- switch ( currentExpression . NodeType )
573
- {
574
- case ExpressionType . Convert :
575
- case ExpressionType . ConvertChecked :
576
- case ExpressionType . TypeAs :
577
- var unaryExpression = ( UnaryExpression ) currentExpression ;
578
- return IsNullable ( unaryExpression . Operand , equalityExpression ) ; // a cast will not return null if the operand is not null
579
- case ExpressionType . Not :
580
- case ExpressionType . And :
581
- case ExpressionType . Or :
582
- case ExpressionType . ExclusiveOr :
583
- case ExpressionType . LeftShift :
584
- case ExpressionType . RightShift :
585
- case ExpressionType . AndAlso :
586
- case ExpressionType . OrElse :
587
- case ExpressionType . Equal :
588
- case ExpressionType . NotEqual :
589
- case ExpressionType . GreaterThanOrEqual :
590
- case ExpressionType . GreaterThan :
591
- case ExpressionType . LessThan :
592
- case ExpressionType . LessThanOrEqual :
593
- return false ;
594
- case ExpressionType . Add :
595
- case ExpressionType . AddChecked :
596
- case ExpressionType . Divide :
597
- case ExpressionType . Modulo :
598
- case ExpressionType . Multiply :
599
- case ExpressionType . MultiplyChecked :
600
- case ExpressionType . Power :
601
- case ExpressionType . Subtract :
602
- case ExpressionType . SubtractChecked :
603
- var binaryExpression = ( BinaryExpression ) currentExpression ;
604
- return IsNullable ( binaryExpression . Left , equalityExpression ) || IsNullable ( binaryExpression . Right , equalityExpression ) ;
605
- case ExpressionType . ArrayIndex :
606
- return true ; // for indexed lists we cannot determine whether the item will be null or not
607
- case ExpressionType . Coalesce :
608
- return IsNullable ( ( ( BinaryExpression ) currentExpression ) . Right , equalityExpression ) ;
609
- case ExpressionType . Conditional :
610
- var conditionalExpression = ( ConditionalExpression ) currentExpression ;
611
- return IsNullable ( conditionalExpression . IfTrue , equalityExpression ) ||
612
- IsNullable ( conditionalExpression . IfFalse , equalityExpression ) ;
613
- case ExpressionType . Call :
614
- var methodInfo = ( ( MethodCallExpression ) currentExpression ) . Method ;
615
- return ! _functionRegistry . TryGetGenerator ( methodInfo , out var method ) || method . AllowsNullableReturnType ( methodInfo ) ;
616
- case ExpressionType . MemberAccess :
617
- var memberExpression = ( MemberExpression ) currentExpression ;
618
-
619
- if ( _functionRegistry . TryGetGenerator ( memberExpression . Member , out _ ) )
620
- {
621
- // We have to skip the property as it will be converted to a function that can return null
622
- // if the argument is null
623
- currentExpression = memberExpression . Expression ;
624
- continue ;
625
- }
626
-
627
- var memberType = ReflectHelper . GetPropertyOrFieldType ( memberExpression . Member ) ;
628
- if ( memberType ? . IsValueType == true && ! memberType . IsNullable ( ) )
629
- {
630
- currentExpression = memberExpression . Expression ;
631
- continue ;
632
- }
633
-
634
- // Check if there was a not null check prior the equality expression
635
- if ( (
636
- equalityExpression . NodeType == ExpressionType . NotEqual ||
637
- equalityExpression . NodeType == ExpressionType . Equal
638
- ) &&
639
- _equalityNotNullMembers . TryGetValue ( equalityExpression , out var notNullMembers ) &&
640
- notNullMembers . Any ( o => AreEqual ( o , memberExpression ) ) )
641
- {
642
- return false ;
643
- }
644
-
645
- // We have to check the member mapping to determine if is nullable
646
- var entityName = TryGetEntityName ( memberExpression ) ;
647
- if ( entityName == null )
648
- {
649
- return true ; // not mapped
650
- }
651
-
652
- var persister = _parameters . SessionFactory . GetEntityPersister ( entityName ) ;
653
- var index = persister . EntityMetamodel . GetPropertyIndexOrNull ( memberExpression . Member . Name ) ;
654
- if ( ! index . HasValue || persister . EntityMetamodel . PropertyNullability [ index . Value ] )
655
- {
656
- return true ; // not mapped or nullable
657
- }
658
-
659
- currentExpression = memberExpression . Expression ;
660
- continue ;
661
- case ExpressionType . Extension :
662
- switch ( currentExpression )
663
- {
664
- case QuerySourceReferenceExpression querySourceReferenceExpression :
665
- switch ( querySourceReferenceExpression . ReferencedQuerySource )
666
- {
667
- case MainFromClause _:
668
- return false ; // we reached to the root expression, there were no nullable expressions
669
- case NhJoinClause joinClause :
670
- return IsNullable ( joinClause . FromExpression , equalityExpression ) ;
671
- default :
672
- return true ; // unknown query source
673
- }
674
- case SubQueryExpression subQuery :
675
- if ( subQuery . QueryModel . SelectClause . Selector is NhAggregatedExpression subQueryAggregatedExpression )
676
- {
677
- return subQueryAggregatedExpression . AllowsNullableReturnType ;
678
- }
679
- else if ( subQuery . QueryModel . ResultOperators . Any ( o => NotNullOperators . Contains ( o . GetType ( ) ) ) )
680
- {
681
- return false ;
682
- }
683
-
684
- return true ;
685
- case NhAggregatedExpression aggregatedExpression :
686
- return aggregatedExpression . AllowsNullableReturnType ;
687
- default :
688
- return true ; // a query can return null and we cannot calculate it as it is not yet executed
689
- }
690
- case ExpressionType . TypeIs : // an equal or in operator will be generated and those cannot return null
691
- case ExpressionType . NewArrayInit :
692
- return false ;
693
- case ExpressionType . Constant :
694
- return VisitorUtil . IsNullConstant ( currentExpression ) ;
695
- case ExpressionType . Parameter :
696
- return ! currentExpression . Type . IsValueType ;
697
- default :
698
- return true ;
699
- }
700
- }
701
- }
702
-
703
- private bool AreEqual ( MemberExpression memberExpression , MemberExpression otherMemberExpression )
704
- {
705
- if ( memberExpression . Member != otherMemberExpression . Member ||
706
- memberExpression . Expression . NodeType != otherMemberExpression . Expression . NodeType )
707
- {
708
- return false ;
709
- }
710
-
711
- switch ( memberExpression . Expression )
712
- {
713
- case QuerySourceReferenceExpression querySourceReferenceExpression :
714
- if ( otherMemberExpression . Expression is QuerySourceReferenceExpression otherQuerySourceReferenceExpression )
715
- {
716
- return querySourceReferenceExpression . ReferencedQuerySource ==
717
- otherQuerySourceReferenceExpression . ReferencedQuerySource ;
718
- }
719
-
720
- return false ;
721
- // Components have a nested member expression
722
- case MemberExpression nestedMemberExpression :
723
- if ( otherMemberExpression . Expression is MemberExpression otherNestedMemberExpression )
724
- {
725
- return AreEqual ( nestedMemberExpression , otherNestedMemberExpression ) ;
726
- }
727
-
728
- return false ;
729
- default :
730
- return memberExpression . Expression == otherMemberExpression . Expression ;
731
- }
732
- }
733
-
734
- private string TryGetEntityName ( MemberExpression memberExpression )
735
- {
736
- System . Type entityType ;
737
- // Try to get the actual entity type from the query source if possbile as member can be declared
738
- // in a base type
739
- if ( memberExpression . Expression is QuerySourceReferenceExpression querySourceReferenceExpression )
740
- {
741
- entityType = querySourceReferenceExpression . Type ;
742
- }
743
- else
744
- {
745
- entityType = memberExpression . Member . ReflectedType ;
746
- }
747
-
748
- return _parameters . SessionFactory . TryGetGuessEntityName ( entityType ) ;
749
- }
750
-
751
465
protected HqlTreeNode VisitUnaryExpression ( UnaryExpression expression )
752
466
{
753
467
switch ( expression . NodeType )
0 commit comments