internal override Expression VisitMemberAccess(MemberExpression m) { Debug.Assert(m != null, "m != null"); this.leafExpressionIsMemberAccess = true; // Only allowed to project entities if (!ClientTypeUtil.TypeOrElementTypeIsEntity(m.Expression.Type) || IsCollectionProducingExpression(m.Expression)) { throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, m.ToString())); } PropertyInfo pi; Expression boundTarget; if (ResourceBinder.PatternRules.MatchNonPrivateReadableProperty(m, out pi, out boundTarget)) { Expression e = base.VisitMemberAccess(m); Type convertedType; ResourceBinder.StripTo <Expression>(m.Expression, out convertedType); box.AppendPropertyToPath(pi, convertedType, this.context); this.leafExpressionIsMemberAccess = false; return(e); } throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, m.ToString())); }
/// <summary> /// Visits a member access expression in non-entity projections, validating that /// it's correct and recording the path visit to include in a projection if necessary. /// </summary> /// <param name="m">Expression to visit.</param> /// <returns>The same expression.</returns> /// <remarks> /// The projection analyzer runs after funcletization, so a member expression /// rather than a constant expression implies that this is correlated to /// a parameter, by dotting through the argument in valid cases, and possibly /// more complex cases in others like new DSC(p.Orders)*.Foo* <- .Foo is invalid. /// </remarks> internal override Expression VisitMemberAccess(MemberExpression m) { Debug.Assert(m != null, "m != null"); Type expressionType = m.Expression.Type; this.leafExpressionIsMemberAccess = true; // if primitive or nullable primitive, allow member access... i.e. calling Value on nullable<int> if (PrimitiveType.IsKnownNullableType(expressionType)) { this.leafExpressionIsMemberAccess = false; return(base.VisitMemberAccess(m)); } // Only allowed to project entities, also it is ok to do client side projections on complex types. // Details on the fix for the Dev11 bug 350541 "Inconsistency between Count() method call and Count property projection on clr type collections": // Relax check to only throw if IsCollectionProducingExpression returns true. // This enables client side projections (for example "Count") on Clr type collections, like ReadOnlyCollection (which is used in spatial types), ICollection, IList, etc. // We already allow client side method calls (like Linq extension method "Count()") on clr type collections, so it makes client side projections consistent. // Note: it will still throw for List<T> (because IsCollectionProducingExpression returns true for List<T>), // however this is consistent with how we handle MethodCallExpression on clr type collections // and changing IsCollectionProducingExpression seems risky at this point as it's used in a lot of places. if (IsCollectionProducingExpression(m.Expression)) { throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString())); } PropertyInfo pi; Expression boundTarget; if (ResourceBinder.PatternRules.MatchNonPrivateReadableProperty(m, out pi, out boundTarget)) { Expression e = base.VisitMemberAccess(m); if (ClientTypeUtil.TypeOrElementTypeIsEntity(expressionType)) { Type convertedType; ResourceBinder.StripTo <Expression>(m.Expression, out convertedType); box.AppendPropertyToPath(pi, convertedType, this.context); this.leafExpressionIsMemberAccess = false; } return(e); } throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString())); }