@@ -22,6 +22,7 @@ public abstract class ComparisonOperator : SmartEnum<ComparisonOperator>
2222 public static ComparisonOperator CaseSensitiveNotStartsWithOperator = new NotStartsWithType ( ) ;
2323 public static ComparisonOperator CaseSensitiveNotEndsWithOperator = new NotEndsWithType ( ) ;
2424 public static ComparisonOperator CaseSensitiveInOperator = new InType ( ) ;
25+ public static ComparisonOperator CaseSensitiveNotInOperator = new NotInType ( ) ;
2526 public static ComparisonOperator CaseSensitiveSoundsLikeOperator = new SoundsLikeType ( ) ;
2627 public static ComparisonOperator CaseSensitiveDoesNotSoundLikeOperator = new DoesNotSoundLikeType ( ) ;
2728 public static ComparisonOperator CaseSensitiveHasCountEqualToOperator = new HasCountEqualToType ( ) ;
@@ -46,6 +47,7 @@ public abstract class ComparisonOperator : SmartEnum<ComparisonOperator>
4647 public static ComparisonOperator NotStartsWithOperator ( bool caseInsensitive = false , bool usesAll = false ) => new NotStartsWithType ( caseInsensitive ) ;
4748 public static ComparisonOperator NotEndsWithOperator ( bool caseInsensitive = false , bool usesAll = false ) => new NotEndsWithType ( caseInsensitive ) ;
4849 public static ComparisonOperator InOperator ( bool caseInsensitive = false , bool usesAll = false ) => new InType ( caseInsensitive ) ;
50+ public static ComparisonOperator NotInOperator ( bool caseInsensitive = false , bool usesAll = false ) => new NotInType ( caseInsensitive ) ;
4951 public static ComparisonOperator SoundsLikeOperator ( bool caseInsensitive = false , bool usesAll = false ) => new SoundsLikeType ( caseInsensitive ) ;
5052 public static ComparisonOperator DoesNotSoundLikeOperator ( bool caseInsensitive = false , bool usesAll = false ) => new DoesNotSoundLikeType ( caseInsensitive ) ;
5153 public static ComparisonOperator HasCountEqualToOperator ( bool caseInsensitive = false , bool usesAll = false ) => new HasCountEqualToType ( caseInsensitive ) ;
@@ -120,6 +122,10 @@ public static ComparisonOperator GetByOperatorString(string op, bool caseInsensi
120122 {
121123 newOperator = new InType ( caseInsensitive , usesAll ) ;
122124 }
125+ if ( comparisonOperator is NotInType )
126+ {
127+ newOperator = new NotInType ( caseInsensitive , usesAll ) ;
128+ }
123129 if ( comparisonOperator is SoundsLikeType )
124130 {
125131 newOperator = new SoundsLikeType ( caseInsensitive , usesAll ) ;
@@ -683,6 +689,54 @@ public override Expression GetExpression<T>(Expression left, Expression right, T
683689 throw new QueryKitParsingException ( "DoesNotHaveType is only supported for collections" ) ;
684690 }
685691 }
692+
693+ private class NotInType : ComparisonOperator
694+ {
695+ public NotInType ( bool caseInsensitive = false , bool usesAll = false ) : base ( "!^^" , 23 , caseInsensitive , usesAll )
696+ {
697+ }
698+
699+ public override string Operator ( ) => CaseInsensitive ? $ "{ Name } { CaseSensitiveAppendix } " : Name ;
700+ public override Expression GetExpression < T > ( Expression left , Expression right , Type ? dbContextType )
701+ {
702+ var leftType = left . Type ;
703+
704+ if ( right is NewArrayExpression newArrayExpression )
705+ {
706+ var listType = typeof ( List < > ) . MakeGenericType ( leftType ) ;
707+ var list = Activator . CreateInstance ( listType ) ;
708+
709+ foreach ( var value in newArrayExpression . Expressions )
710+ {
711+ listType . GetMethod ( "Add" ) . Invoke ( list , new [ ] { ( ( ConstantExpression ) value ) . Value } ) ;
712+ }
713+
714+ right = Expression . Constant ( list , listType ) ;
715+ }
716+
717+ // Get the Contains method with the correct generic type
718+ var containsMethod = typeof ( ICollection < > )
719+ . MakeGenericType ( leftType )
720+ . GetMethod ( "Contains" ) ;
721+
722+ if ( CaseInsensitive && leftType == typeof ( string ) )
723+ {
724+ var listType = typeof ( List < string > ) ;
725+ var toLowerList = Activator . CreateInstance ( listType ) ;
726+
727+ var originalList = ( ( ConstantExpression ) right ) . Value as IEnumerable < string > ;
728+ foreach ( var value in originalList )
729+ {
730+ listType . GetMethod ( "Add" ) . Invoke ( toLowerList , new [ ] { value . ToLower ( ) } ) ;
731+ }
732+ right = Expression . Constant ( toLowerList , listType ) ;
733+ left = Expression . Call ( left , typeof ( string ) . GetMethod ( "ToLower" , Type . EmptyTypes ) ) ;
734+ }
735+
736+ var containsExpression = Expression . Call ( right , containsMethod , left ) ;
737+ return Expression . Not ( containsExpression ) ;
738+ }
739+ }
686740
687741 internal class ComparisonAliasMatch
688742 {
@@ -759,6 +813,11 @@ internal static List<ComparisonAliasMatch> GetAliasMatches(IQueryKitConfiguratio
759813 matches . Add ( new ComparisonAliasMatch { Alias = aliases . InOperator , Operator = InOperator ( ) . Operator ( ) } ) ;
760814 matches . Add ( new ComparisonAliasMatch { Alias = $ "{ aliases . InOperator } { caseInsensitiveAppendix } ", Operator = $ "{ InOperator ( true ) . Operator ( ) } " } ) ;
761815 }
816+ if ( aliases . NotInOperator != NotInOperator ( ) . Operator ( ) )
817+ {
818+ matches . Add ( new ComparisonAliasMatch { Alias = aliases . NotInOperator , Operator = NotInOperator ( ) . Operator ( ) } ) ;
819+ matches . Add ( new ComparisonAliasMatch { Alias = $ "{ aliases . NotInOperator } { caseInsensitiveAppendix } ", Operator = $ "{ NotInOperator ( true ) . Operator ( ) } " } ) ;
820+ }
762821 if ( aliases . HasCountEqualToOperator != HasCountEqualToOperator ( ) . Operator ( ) )
763822 {
764823 matches . Add ( new ComparisonAliasMatch { Alias = aliases . HasCountEqualToOperator , Operator = HasCountEqualToOperator ( ) . Operator ( ) } ) ;
0 commit comments