@@ -117,7 +117,7 @@ private static IType GetCandidateType(
117
117
if ( ! ExpressionsHelper . TryGetMappedType ( sessionFactory , relatedExpression , out var mappedType , out _ , out _ , out _ ) )
118
118
continue ;
119
119
120
- if ( mappedType . IsAssociationType && visitor . SequenceSelectorExpressions . Contains ( relatedExpression ) )
120
+ if ( mappedType . IsCollectionType )
121
121
{
122
122
var collection = ( IQueryableCollection ) ( ( IAssociationType ) mappedType ) . GetAssociatedJoinable ( sessionFactory ) ;
123
123
mappedType = collection . ElementType ;
@@ -199,7 +199,6 @@ private class ConstantTypeLocatorVisitor : RelinqExpressionVisitor
199
199
new Dictionary < NamedParameter , HashSet < ConstantExpression > > ( ) ;
200
200
public readonly Dictionary < Expression , HashSet < Expression > > RelatedExpressions =
201
201
new Dictionary < Expression , HashSet < Expression > > ( ) ;
202
- public readonly HashSet < Expression > SequenceSelectorExpressions = new HashSet < Expression > ( ) ;
203
202
204
203
public ConstantTypeLocatorVisitor (
205
204
bool removeMappedAsCalls ,
@@ -305,41 +304,53 @@ protected override Expression VisitConstant(ConstantExpression node)
305
304
}
306
305
307
306
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 )
308
317
{
309
318
// ReLinq wraps all ResultOperatorExpressionNodeBase into a SubQueryExpression. In case of
310
319
// ContainsResultOperator where the constant expression is dislocated from the related expression,
311
320
// 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 ) )
317
325
{
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 ) ) ;
329
342
}
330
343
else
331
344
{
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 ;
340
346
}
341
347
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 ;
343
354
}
344
355
345
356
private void VisitAssign ( Expression leftNode , Expression rightNode )
@@ -369,7 +380,7 @@ private void AddRelatedExpression(Expression node, Expression left, Expression r
369
380
left is QuerySourceReferenceExpression )
370
381
{
371
382
AddRelatedExpression ( right , left ) ;
372
- if ( NonVoidOperators . Contains ( node . NodeType ) )
383
+ if ( node != null && NonVoidOperators . Contains ( node . NodeType ) )
373
384
{
374
385
AddRelatedExpression ( node , left ) ;
375
386
}
@@ -382,7 +393,7 @@ private void AddRelatedExpression(Expression node, Expression left, Expression r
382
393
foreach ( var nestedMemberExpression in set )
383
394
{
384
395
AddRelatedExpression ( right , nestedMemberExpression ) ;
385
- if ( NonVoidOperators . Contains ( node . NodeType ) )
396
+ if ( node != null && NonVoidOperators . Contains ( node . NodeType ) )
386
397
{
387
398
AddRelatedExpression ( node , nestedMemberExpression ) ;
388
399
}
0 commit comments