Skip to content

Commit 48efdbd

Browse files
committed
Improved parameter detection on TypeAs translation
1 parent 2b369fb commit 48efdbd

1 file changed

Lines changed: 32 additions & 15 deletions

File tree

Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,41 +1505,56 @@ private Expression GetConditionalMember(Expression expression, MemberInfo member
15051505
/// <exception cref="InvalidOperationException"><c>InvalidOperationException</c>.</exception>
15061506
private Expression VisitTypeAs(Expression source, Type targetType)
15071507
{
1508-
if (source.GetMemberType()!=MemberType.Entity)
1508+
if (source.GetMemberType() != MemberType.Entity) {
15091509
throw new NotSupportedException(Strings.ExAsOperatorSupportsEntityOnly);
1510+
}
15101511

15111512
// Expression is already of requested type.
1512-
var visitedSource = Visit(source);
1513-
if (source.Type==targetType)
1514-
return visitedSource;
1513+
if (source.Type == targetType) {
1514+
return Visit(source);
1515+
}
15151516

15161517
// Call convert to parent type.
1517-
if (targetType.IsAssignableFrom(source.Type))
1518+
if (targetType.IsAssignableFrom(source.Type)) {
15181519
return Visit(Expression.Convert(source, targetType));
1520+
}
15191521

15201522
// Cast to subclass or interface.
15211523
using (state.CreateScope()) {
1522-
var targetTypeInfo = context.Model.Types[targetType];
1524+
var bareVisitedSource = Visit(source).StripCasts().StripMarkers();
1525+
if (!(bareVisitedSource is IEntityExpression entityExpression))
1526+
throw new InvalidOperationException(Strings.ExAsOperatorSupportsEntityOnly);
1527+
15231528
// Using of state.Parameter[0] is a very weak approach.
15241529
// `as` operator could be applied on expression that has no relation with current parameter
15251530
// thus the later code will fail.
15261531
// We can't easily find real parameter that need replacement.
15271532
// We work around this situation by supporting some known cases.
15281533
// The simplest (and the only at moment) case is a source being chain of MemberExpressions.
1534+
// Some known cases also can be solved by using outer parameter of visited source which is in form of IEntityExpression
1535+
1536+
var strippedSource = source.StripMemberAccessChain();
15291537
var currentParameter = state.Parameters[0];
1530-
var parameter = (source.StripMemberAccessChain() as ParameterExpression) ?? currentParameter;
1531-
var entityExpression = visitedSource.StripCasts().StripMarkers() as IEntityExpression;
15321538

1533-
if (entityExpression==null)
1534-
throw new InvalidOperationException(Strings.ExAsOperatorSupportsEntityOnly);
1539+
ParameterExpression parameter;
1540+
if (strippedSource is ParameterExpression pExpression) {
1541+
parameter = pExpression;
1542+
}
1543+
else if (bareVisitedSource is ParameterizedExpression pzExpression && pzExpression.OuterParameter != null) {
1544+
parameter = pzExpression.OuterParameter;
1545+
}
1546+
else {
1547+
parameter = currentParameter;
1548+
}
15351549

15361550
// Replace original recordset. New recordset is left join with old recordset
1537-
ProjectionExpression originalResultExpression = context.Bindings[parameter];
1551+
var originalResultExpression = context.Bindings[parameter];
15381552
var originalQuery = originalResultExpression.ItemProjector.DataSource;
1539-
int offset = originalQuery.Header.Columns.Count;
1553+
var offset = originalQuery.Header.Columns.Count;
15401554

15411555
// Join primary index of target type
1542-
IndexInfo indexToJoin = targetTypeInfo.Indexes.PrimaryIndex;
1556+
var targetTypeInfo = context.Model.Types[targetType];
1557+
var indexToJoin = targetTypeInfo.Indexes.PrimaryIndex;
15431558
var queryToJoin = indexToJoin.GetQuery().Alias(context.GetNextAlias());
15441559
var keySegment = entityExpression.Key.Mapping.GetItems();
15451560
var keyPairs = keySegment
@@ -1555,10 +1570,12 @@ private Expression VisitTypeAs(Expression source, Type targetType)
15551570
context.Bindings.ReplaceBound(parameter, projectionExpression);
15561571

15571572
// return new EntityExpression
1558-
var result = EntityExpression.Create(context.Model.Types[targetType], offset, false);
1573+
var result = EntityExpression.Create(targetTypeInfo, offset, false);
15591574
result.IsNullable = true;
1560-
if (parameter!=currentParameter)
1575+
if (parameter != currentParameter) {
15611576
result = (EntityExpression) result.BindParameter(parameter, new Dictionary<Expression, Expression>());
1577+
}
1578+
15621579
return result;
15631580
}
15641581
}

0 commit comments

Comments
 (0)