@@ -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 ;
@@ -176,7 +176,6 @@ private class ConstantTypeLocatorVisitor : RelinqExpressionVisitor
176
176
new Dictionary < NamedParameter , HashSet < ConstantExpression > > ( ) ;
177
177
public readonly Dictionary < Expression , HashSet < Expression > > RelatedExpressions =
178
178
new Dictionary < Expression , HashSet < Expression > > ( ) ;
179
- public readonly HashSet < Expression > SequenceSelectorExpressions = new HashSet < Expression > ( ) ;
180
179
181
180
public ConstantTypeLocatorVisitor (
182
181
bool removeMappedAsCalls ,
@@ -282,41 +281,43 @@ protected override Expression VisitConstant(ConstantExpression node)
282
281
}
283
282
284
283
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 )
285
294
{
286
295
// ReLinq wraps all ResultOperatorExpressionNodeBase into a SubQueryExpression. In case of
287
296
// ContainsResultOperator where the constant expression is dislocated from the related expression,
288
297
// 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 ) )
294
302
{
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 ;
306
304
}
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
- }
315
305
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 ;
317
313
}
318
314
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 ;
320
321
}
321
322
322
323
private void VisitAssign ( Expression leftNode , Expression rightNode )
@@ -346,7 +347,7 @@ private void AddRelatedExpression(Expression node, Expression left, Expression r
346
347
left is QuerySourceReferenceExpression )
347
348
{
348
349
AddRelatedExpression ( right , left ) ;
349
- if ( NonVoidOperators . Contains ( node . NodeType ) )
350
+ if ( node != null && NonVoidOperators . Contains ( node . NodeType ) )
350
351
{
351
352
AddRelatedExpression ( node , left ) ;
352
353
}
@@ -359,7 +360,7 @@ private void AddRelatedExpression(Expression node, Expression left, Expression r
359
360
foreach ( var nestedMemberExpression in set )
360
361
{
361
362
AddRelatedExpression ( right , nestedMemberExpression ) ;
362
- if ( NonVoidOperators . Contains ( node . NodeType ) )
363
+ if ( node != null && NonVoidOperators . Contains ( node . NodeType ) )
363
364
{
364
365
AddRelatedExpression ( node , nestedMemberExpression ) ;
365
366
}
0 commit comments