@@ -117,7 +117,7 @@ private static IType GetCandidateType(
117117 if ( ! ExpressionsHelper . TryGetMappedType ( sessionFactory , relatedExpression , out var mappedType , out _ , out _ , out _ ) )
118118 continue ;
119119
120- if ( mappedType . IsAssociationType && visitor . SequenceSelectorExpressions . Contains ( relatedExpression ) )
120+ if ( mappedType . IsCollectionType )
121121 {
122122 var collection = ( IQueryableCollection ) ( ( IAssociationType ) mappedType ) . GetAssociatedJoinable ( sessionFactory ) ;
123123 mappedType = collection . ElementType ;
@@ -176,7 +176,6 @@ private class ConstantTypeLocatorVisitor : RelinqExpressionVisitor
176176 new Dictionary < NamedParameter , HashSet < ConstantExpression > > ( ) ;
177177 public readonly Dictionary < Expression , HashSet < Expression > > RelatedExpressions =
178178 new Dictionary < Expression , HashSet < Expression > > ( ) ;
179- public readonly HashSet < Expression > SequenceSelectorExpressions = new HashSet < Expression > ( ) ;
180179
181180 public ConstantTypeLocatorVisitor (
182181 bool removeMappedAsCalls ,
@@ -282,41 +281,43 @@ protected override Expression VisitConstant(ConstantExpression node)
282281 }
283282
284283 protected override Expression VisitSubQuery ( SubQueryExpression node )
284+ {
285+ if ( ! TryLinkContainsMethod ( node . QueryModel ) )
286+ {
287+ node . QueryModel . TransformExpressions ( Visit ) ;
288+ }
289+
290+ return node ;
291+ }
292+
293+ private bool TryLinkContainsMethod ( QueryModel queryModel )
285294 {
286295 // ReLinq wraps all ResultOperatorExpressionNodeBase into a SubQueryExpression. In case of
287296 // ContainsResultOperator where the constant expression is dislocated from the related expression,
288297 // we have to manually link the related expressions.
289- if ( node . QueryModel . ResultOperators . Count == 1 &&
290- node . QueryModel . ResultOperators [ 0 ] is ContainsResultOperator containsOperator &&
291- node . QueryModel . SelectClause . Selector is QuerySourceReferenceExpression querySourceReference &&
292- querySourceReference . ReferencedQuerySource is MainFromClause mainFromClause &&
293- mainFromClause . FromExpression is ConstantExpression constantExpression )
298+ if ( queryModel . ResultOperators . Count != 1 ||
299+ ! ( queryModel . ResultOperators [ 0 ] is ContainsResultOperator containsOperator ) ||
300+ ! ( queryModel . SelectClause . Selector is QuerySourceReferenceExpression querySourceReference ) ||
301+ ! ( querySourceReference . ReferencedQuerySource is MainFromClause mainFromClause ) )
294302 {
295- VisitConstant ( constantExpression ) ;
296- AddRelatedExpression ( constantExpression , UnwrapUnary ( Visit ( containsOperator . Item ) ) ) ;
297- // Copy all found MemberExpressions to the constant expression
298- // (e.g. values.Contains(o.Name != o.Name2 ? o.Enum1 : o.Enum2) -> copy o.Enum1 and o.Enum2)
299- if ( RelatedExpressions . TryGetValue ( containsOperator . Item , out var set ) )
300- {
301- foreach ( var nestedMemberExpression in set )
302- {
303- AddRelatedExpression ( constantExpression , nestedMemberExpression ) ;
304- }
305- }
303+ return false ;
306304 }
307- else
308- {
309- // In case a parameter is related to a sequence selector we will have to get the underlying item type
310- // (e.g. q.Where(o => o.Users.Any(u => u == user)))
311- if ( node . QueryModel . ResultOperators . Any ( o => o is ValueFromSequenceResultOperatorBase ) )
312- {
313- SequenceSelectorExpressions . Add ( node . QueryModel . SelectClause . Selector ) ;
314- }
315305
316- node . QueryModel . TransformExpressions ( Visit ) ;
306+ var left = UnwrapUnary ( Visit ( mainFromClause . FromExpression ) ) ;
307+ var right = UnwrapUnary ( Visit ( containsOperator . Item ) ) ;
308+ // The constant is on the left side (e.g. db.Users.Where(o => users.Contains(o)))
309+ // The constant is on the right side (e.g. db.Customers.Where(o => o.Orders.Contains(item)))
310+ if ( left . NodeType != ExpressionType . Constant && right . NodeType != ExpressionType . Constant )
311+ {
312+ return false ;
317313 }
318314
319- return node ;
315+ // Copy all found MemberExpressions to the constant expression
316+ // (e.g. values.Contains(o.Name != o.Name2 ? o.Enum1 : o.Enum2) -> copy o.Enum1 and o.Enum2)
317+ AddRelatedExpression ( null , left , right ) ;
318+ AddRelatedExpression ( null , right , left ) ;
319+
320+ return true ;
320321 }
321322
322323 private void VisitAssign ( Expression leftNode , Expression rightNode )
@@ -346,7 +347,7 @@ private void AddRelatedExpression(Expression node, Expression left, Expression r
346347 left is QuerySourceReferenceExpression )
347348 {
348349 AddRelatedExpression ( right , left ) ;
349- if ( NonVoidOperators . Contains ( node . NodeType ) )
350+ if ( node != null && NonVoidOperators . Contains ( node . NodeType ) )
350351 {
351352 AddRelatedExpression ( node , left ) ;
352353 }
@@ -359,7 +360,7 @@ private void AddRelatedExpression(Expression node, Expression left, Expression r
359360 foreach ( var nestedMemberExpression in set )
360361 {
361362 AddRelatedExpression ( right , nestedMemberExpression ) ;
362- if ( NonVoidOperators . Contains ( node . NodeType ) )
363+ if ( node != null && NonVoidOperators . Contains ( node . NodeType ) )
363364 {
364365 AddRelatedExpression ( node , nestedMemberExpression ) ;
365366 }
0 commit comments