@@ -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 ;
@@ -199,7 +199,6 @@ private class ConstantTypeLocatorVisitor : RelinqExpressionVisitor
199199 new Dictionary < NamedParameter , HashSet < ConstantExpression > > ( ) ;
200200 public readonly Dictionary < Expression , HashSet < Expression > > RelatedExpressions =
201201 new Dictionary < Expression , HashSet < Expression > > ( ) ;
202- public readonly HashSet < Expression > SequenceSelectorExpressions = new HashSet < Expression > ( ) ;
203202
204203 public ConstantTypeLocatorVisitor (
205204 bool removeMappedAsCalls ,
@@ -305,41 +304,53 @@ protected override Expression VisitConstant(ConstantExpression node)
305304 }
306305
307306 protected override Expression VisitSubQuery ( SubQueryExpression node )
307+ {
308+ if ( ! TryLinkContainsMethod ( node . QueryModel ) )
309+ {
310+ node . QueryModel . TransformExpressions ( Visit ) ;
311+ }
312+
313+ return node ;
314+ }
315+
316+ private bool TryLinkContainsMethod ( QueryModel queryModel )
308317 {
309318 // ReLinq wraps all ResultOperatorExpressionNodeBase into a SubQueryExpression. In case of
310319 // ContainsResultOperator where the constant expression is dislocated from the related expression,
311320 // we have to manually link the related expressions.
312- if ( node . QueryModel . ResultOperators . Count == 1 &&
313- node . QueryModel . ResultOperators [ 0 ] is ContainsResultOperator containsOperator &&
314- node . QueryModel . SelectClause . Selector is QuerySourceReferenceExpression querySourceReference &&
315- querySourceReference . ReferencedQuerySource is MainFromClause mainFromClause &&
316- mainFromClause . FromExpression is ConstantExpression constantExpression )
321+ if ( queryModel . ResultOperators . Count != 1 ||
322+ ! ( queryModel . ResultOperators [ 0 ] is ContainsResultOperator containsOperator ) ||
323+ ! ( queryModel . SelectClause . Selector is QuerySourceReferenceExpression querySourceReference ) ||
324+ ! ( querySourceReference . ReferencedQuerySource is MainFromClause mainFromClause ) )
317325 {
318- VisitConstant ( constantExpression ) ;
319- AddRelatedExpression ( constantExpression , UnwrapUnary ( Visit ( containsOperator . Item ) ) ) ;
320- // Copy all found MemberExpressions to the constant expression
321- // (e.g. values.Contains(o.Name != o.Name2 ? o.Enum1 : o.Enum2) -> copy o.Enum1 and o.Enum2)
322- if ( RelatedExpressions . TryGetValue ( containsOperator . Item , out var set ) )
323- {
324- foreach ( var nestedMemberExpression in set )
325- {
326- AddRelatedExpression ( constantExpression , nestedMemberExpression ) ;
327- }
328- }
326+ return false ;
327+ }
328+
329+ var left = UnwrapUnary ( mainFromClause . FromExpression ) ;
330+ var right = UnwrapUnary ( containsOperator . Item ) ;
331+ if ( left . NodeType == ExpressionType . Constant )
332+ {
333+ // The constant is on the left side (e.g. db.Users.Where(o => users.Contains(o)))
334+ VisitConstant ( ( ConstantExpression ) left ) ;
335+ right = UnwrapUnary ( Visit ( containsOperator . Item ) ) ;
336+ }
337+ else if ( right . NodeType == ExpressionType . Constant )
338+ {
339+ // The constant is on the right side (e.g. db.Customers.Where(o => o.Orders.Contains(item)))
340+ VisitConstant ( ( ConstantExpression ) right ) ;
341+ left = UnwrapUnary ( Visit ( mainFromClause . FromExpression ) ) ;
329342 }
330343 else
331344 {
332- // In case a parameter is related to a sequence selector we will have to get the underlying item type
333- // (e.g. q.Where(o => o.Users.Any(u => u == user)))
334- if ( node . QueryModel . ResultOperators . Any ( o => o is ValueFromSequenceResultOperatorBase ) )
335- {
336- SequenceSelectorExpressions . Add ( node . QueryModel . SelectClause . Selector ) ;
337- }
338-
339- node . QueryModel . TransformExpressions ( Visit ) ;
345+ return false ;
340346 }
341347
342- return node ;
348+ // Copy all found MemberExpressions to the constant expression
349+ // (e.g. values.Contains(o.Name != o.Name2 ? o.Enum1 : o.Enum2) -> copy o.Enum1 and o.Enum2)
350+ AddRelatedExpression ( null , left , right ) ;
351+ AddRelatedExpression ( null , right , left ) ;
352+
353+ return true ;
343354 }
344355
345356 private void VisitAssign ( Expression leftNode , Expression rightNode )
@@ -369,7 +380,7 @@ private void AddRelatedExpression(Expression node, Expression left, Expression r
369380 left is QuerySourceReferenceExpression )
370381 {
371382 AddRelatedExpression ( right , left ) ;
372- if ( NonVoidOperators . Contains ( node . NodeType ) )
383+ if ( node != null && NonVoidOperators . Contains ( node . NodeType ) )
373384 {
374385 AddRelatedExpression ( node , left ) ;
375386 }
@@ -382,7 +393,7 @@ private void AddRelatedExpression(Expression node, Expression left, Expression r
382393 foreach ( var nestedMemberExpression in set )
383394 {
384395 AddRelatedExpression ( right , nestedMemberExpression ) ;
385- if ( NonVoidOperators . Contains ( node . NodeType ) )
396+ if ( node != null && NonVoidOperators . Contains ( node . NodeType ) )
386397 {
387398 AddRelatedExpression ( node , nestedMemberExpression ) ;
388399 }
0 commit comments