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); this.builder.AppendPropertyToPath(pi, convertedType, this.context); this.leafExpressionIsMemberAccess = false; return(e); } throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, m.ToString())); }
internal override Expression VisitConditional(ConditionalExpression conditional) { Expression expressionBeforeNormalization = this.GetExpressionBeforeNormalization(conditional); if (expressionBeforeNormalization != conditional) { return(this.Visit(expressionBeforeNormalization)); } ResourceBinder.PatternRules.MatchNullCheckResult nullCheck = ResourceBinder.PatternRules.MatchNullCheck(this.pathBuilder.LambdaParameterInScope, conditional); if (!nullCheck.Match || !ClientTypeUtil.TypeOrElementTypeIsEntity(ResourceBinder.StripConvertToAssignable(nullCheck.TestToNullExpression).Type)) { Expression test = null; if (nullCheck.Match) { Expression left = this.Visit(nullCheck.TestToNullExpression); if (left.NodeType == ExpressionType.Convert) { left = ((UnaryExpression)left).Operand; } test = Expression.MakeBinary(ExpressionType.Equal, left, Expression.Constant(null)); } if (test == null) { test = this.Visit(conditional.Test); } Expression ifTrue = this.Visit(conditional.IfTrue); Expression ifFalse = this.Visit(conditional.IfFalse); if (((test != conditional.Test) || (ifTrue != conditional.IfTrue)) || (ifFalse != conditional.IfFalse)) { return(Expression.Condition(test, ifTrue, ifFalse, ifTrue.Type.IsAssignableFrom(ifFalse.Type) ? ifTrue.Type : ifFalse.Type)); } } return(this.RebindConditionalNullCheck(conditional, nullCheck)); }
/// <summary>Builds the Uri for the expression passed in.</summary> /// <param name="e">The expression to translate into a Uri</param> /// <returns>Query components</returns> internal QueryComponents Translate(Expression e) { Uri uri; Version version; bool addTrailingParens = false; Dictionary <Expression, Expression> normalizerRewrites = null; // short cut analysis if just a resource set or singleton resource. // note - to be backwards compatible with V1, will only append trailing () for queries // that include more then just a resource set. if (!(e is QueryableResourceExpression)) { normalizerRewrites = new Dictionary <Expression, Expression>(ReferenceEqualityComparer <Expression> .Instance); e = Evaluator.PartialEval(e); e = ExpressionNormalizer.Normalize(e, normalizerRewrites); e = ResourceBinder.Bind(e, this.Context); addTrailingParens = true; } UriWriter.Translate(this.Context, addTrailingParens, e, out uri, out version); ResourceExpression re = e as ResourceExpression; Type lastSegmentType = re.Projection == null ? re.ResourceType : re.Projection.Selector.Parameters[0].Type; LambdaExpression selector = re.Projection == null ? null : re.Projection.Selector; return(new QueryComponents(uri, version, lastSegmentType, selector, normalizerRewrites)); }
internal override Expression VisitMemberAccess(MemberExpression m) { PropertyInfo info; Expression expression; Type type = m.Expression.Type; this.leafExpressionIsMemberAccess = true; if (PrimitiveType.IsKnownNullableType(type)) { this.leafExpressionIsMemberAccess = false; return(base.VisitMemberAccess(m)); } if (ProjectionAnalyzer.IsCollectionProducingExpression(m.Expression)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString())); } if (!ResourceBinder.PatternRules.MatchNonPrivateReadableProperty(m, out info, out expression)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString())); } Expression expression2 = base.VisitMemberAccess(m); if (ClientTypeUtil.TypeOrElementTypeIsEntity(type)) { Type type2; ResourceBinder.StripTo <Expression>(m.Expression, out type2); this.box.AppendPropertyToPath(info, type2, this.context); this.leafExpressionIsMemberAccess = false; } return(expression2); }
/// <summary> /// QueryableResourceExpression visit method. /// </summary> /// <param name="rse">QueryableResourceExpression expression to visit</param> /// <returns>Visited QueryableResourceExpression expression</returns> internal override Expression VisitQueryableResourceExpression(QueryableResourceExpression rse) { if ((ResourceExpressionType)rse.NodeType == ResourceExpressionType.ResourceNavigationProperty) { if (rse.IsOperationInvocation && !(rse.Source is QueryableResourceExpression)) { var normalizerRewrites = new Dictionary <Expression, Expression>(ReferenceEqualityComparer <Expression> .Instance); var e = Evaluator.PartialEval(rse.Source); e = ExpressionNormalizer.Normalize(e, normalizerRewrites); e = ResourceBinder.Bind(e, this.context); this.Visit(e); } else { this.Visit(rse.Source); } this.uriBuilder.Append(UriHelper.FORWARDSLASH).Append(this.ExpressionToString(rse.MemberExpression, /*inPath*/ true)); } else if (rse.MemberExpression != null) { // this is a resource set expression // we should be at the very beginning of // the URI Debug.Assert(this.uriBuilder.Length == 0, "The builder is not empty while we are adding a resourceset"); string entitySetName = (String)((ConstantExpression)rse.MemberExpression).Value; this.uriBuilder.Append(this.context.BaseUriResolver.GetEntitySetUri(entitySetName)); } else { this.uriBuilder.Append(this.context.BaseUriResolver.BaseUriOrNull); } WebUtil.RaiseVersion(ref this.uriVersion, rse.UriVersion); if (rse.ResourceTypeAs != null) { this.uriBuilder.Append(UriHelper.FORWARDSLASH); UriHelper.AppendTypeSegment(this.uriBuilder, rse.ResourceTypeAs, this.context, /*inPath*/ true, ref this.uriVersion); } if (rse.KeyPredicateConjuncts.Count > 0) { this.context.UrlKeyDelimiter.AppendKeyExpression(rse.GetKeyProperties(), kvp => ClientTypeUtil.GetServerDefinedName(kvp.Key), kvp => kvp.Value.Value, this.uriBuilder); } if (rse.IsOperationInvocation) { this.VisitOperationInvocation(rse); } if (rse.CountOption == CountOption.CountSegment) { // append $count segment: /$count this.uriBuilder.Append(UriHelper.FORWARDSLASH).Append(UriHelper.DOLLARSIGN).Append(UriHelper.COUNT); } this.VisitQueryOptions(rse); return(rse); }
internal ProjectionPathSegment(ProjectionPath startPath, MemberExpression memberExpression) { this.StartPath = startPath; Expression expression = ResourceBinder.StripTo <Expression>(memberExpression.Expression); this.Member = memberExpression.Member.Name; this.ProjectionType = memberExpression.Type; this.SourceTypeAs = (expression.NodeType == ExpressionType.TypeAs) ? expression.Type : null; }
/// <summary>Gets a rewrite for the specified expression; null if none is found.</summary> /// <param name="expression">Expression to match.</param> /// <returns>A rewrite for the expression; possibly null.</returns> internal Expression GetRewrite(Expression expression) { Debug.Assert(expression != null, "expression != null"); List <string> names = new List <string>(); // This could be a MemberAccess wrapped in a type conversion, so strip off unnecessary conversions for each loop iteration. // E.g. (p.BestFriend As Person) instead of just p.BestFriend expression = ResourceBinder.StripTo <Expression>(expression); while (expression.NodeType == ExpressionType.MemberAccess || expression.NodeType == ExpressionType.TypeAs) { if (expression.NodeType == ExpressionType.MemberAccess) { MemberExpression member = (MemberExpression)expression; names.Add(member.Member.Name); expression = ResourceBinder.StripTo <Expression>(member.Expression); } else { expression = ResourceBinder.StripTo <Expression>(((UnaryExpression)expression).Operand); } } Expression result = null; foreach (var rewrite in this.rewrites) { if (rewrite.Root != expression) { continue; } if (names.Count != rewrite.MemberNames.Length) { continue; } bool match = true; for (int i = 0; i < names.Count && i < rewrite.MemberNames.Length; i++) { if (names[names.Count - i - 1] != rewrite.MemberNames[i]) { match = false; break; } } if (match) { result = rewrite.RewriteExpression; break; } } return(result); }
internal static void CheckChainedSequence(MethodCallExpression call, Type type) { if (ReflectionUtil.IsSequenceSelectMethod(call.Method)) { MethodCallExpression expression = ResourceBinder.StripTo <MethodCallExpression>(call.Arguments[0]); if ((expression != null) && ReflectionUtil.IsSequenceSelectMethod(expression.Method)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(type, call.ToString())); } } }
internal static void CheckChainedSequence(MethodCallExpression call, Type type) { if (ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.Select)) { MethodCallExpression methodCallExpression = ResourceBinder.StripTo <MethodCallExpression>(call.Arguments[0]); if (methodCallExpression != null && ReflectionUtil.IsSequenceMethod(methodCallExpression.Method, SequenceMethod.Select)) { throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Constructing or initializing instances of the type {0} with the expression {1} is not supported.", type, call.ToString())); } } }
internal static void CheckChainedSequence(MethodCallExpression call, Type type) { if (ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.Select)) { MethodCallExpression insideCall = ResourceBinder.StripTo <MethodCallExpression>(call.Arguments[0]); if (insideCall != null && ReflectionUtil.IsSequenceMethod(insideCall.Method, SequenceMethod.Select)) { throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(type, call.ToString())); } } }
internal override Expression VisitMemberAccess(MemberExpression m) { Type type; Expression expression = base.VisitMemberAccess(m); Expression item = ResourceBinder.StripTo <Expression>(m.Expression, out type); if (this.pathFromEntity.Contains(item)) { this.pathFromEntity.Add(m); } return(expression); }
/// <summary>Initializes a new <see cref="ProjectionPathSegment"/> instance.</summary> /// <param name="startPath">Path on which this segment is located.</param> /// <param name="memberExpression">Member expression for the projection path; possibly null.</param> internal ProjectionPathSegment(ProjectionPath startPath, MemberExpression memberExpression) { Debug.Assert(startPath != null, "startPath != null"); Debug.Assert(memberExpression != null, "memberExpression != null"); this.StartPath = startPath; Expression source = ResourceBinder.StripTo <Expression>(memberExpression.Expression); this.Member = ClientTypeUtil.GetServerDefinedName(memberExpression.Member); this.ProjectionType = memberExpression.Type; this.SourceTypeAs = source.NodeType == ExpressionType.TypeAs ? source.Type : null; }
/// <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); this.builder.AppendPropertyToPath(pi, convertedType, this.context); this.leafExpressionIsMemberAccess = false; } return(e); } throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString())); }
/// <summary>Visits a member access expression.</summary> /// <param name="m">Access to visit.</param> /// <returns>The same expression.</returns> internal override Expression VisitMemberAccess(MemberExpression m) { Expression result = base.VisitMemberAccess(m); // Remove any unnecessary type conversions before checking to see if this expression is in the path. // For this purpose we should consider an expression to be equal to that same expression with an unnecessary conversion. // E.g. (p as Person) is the same as just p Type convertedType; Expression strippedExp = ResourceBinder.StripTo <Expression>(m.Expression, out convertedType); if (this.pathFromEntity.Contains(strippedExp)) { this.pathFromEntity.Add(m); } return(result); }
/// <summary> /// Converts expression to Uri /// </summary> /// <param name="e">The expression</param> /// <returns>Converted uri</returns> public virtual Uri Convert(Expression e) { Uri uri; Dictionary <Expression, Expression> rewrites = null; if (!(e is ResourceSetExpression)) { rewrites = new Dictionary <Expression, Expression>(ReferenceEqualityComparer <Expression> .Instance); e = Evaluator.PartialEval(e); e = ExpressionNormalizer.Normalize(e, rewrites); e = ResourceBinder.Bind(e); } UriWriter.Translate(false, e, out uri); return(uri); }
internal QueryComponents Translate(Expression e) { Uri uri; Version version; bool addTrailingParens = false; Dictionary <Expression, Expression> rewrites = null; if (!(e is ResourceSetExpression)) { rewrites = new Dictionary <Expression, Expression>(ReferenceEqualityComparer <Expression> .Instance); e = Evaluator.PartialEval(e); e = ExpressionNormalizer.Normalize(e, rewrites); e = ResourceBinder.Bind(e, this.Context); addTrailingParens = true; } UriWriter.Translate(this.Context, addTrailingParens, e, out uri, out version); ResourceExpression expression = e as ResourceExpression; Type lastSegmentType = (expression.Projection == null) ? expression.ResourceType : expression.Projection.Selector.Parameters[0].Type; return(new QueryComponents(uri, version, lastSegmentType, (expression.Projection == null) ? null : expression.Projection.Selector, rewrites)); }
internal Expression GetRewrite(Expression expression) { List <string> list = new List <string>(); expression = ResourceBinder.StripTo <Expression>(expression); while ((expression.NodeType == ExpressionType.MemberAccess) || (expression.NodeType == ExpressionType.TypeAs)) { if (expression.NodeType == ExpressionType.MemberAccess) { MemberExpression expression2 = (MemberExpression)expression; list.Add(expression2.Member.Name); expression = ResourceBinder.StripTo <Expression>(expression2.Expression); } else { expression = ResourceBinder.StripTo <Expression>(((UnaryExpression)expression).Operand); } } foreach (MemberInitRewrite rewrite in this.rewrites) { if ((rewrite.Root != expression) || (list.Count != rewrite.MemberNames.Length)) { continue; } bool flag = true; for (int i = 0; (i < list.Count) && (i < rewrite.MemberNames.Length); i++) { if (list[(list.Count - i) - 1] != rewrite.MemberNames[i]) { flag = false; break; } } if (flag) { return(rewrite.RewriteExpression); } } return(null); }
internal ExecutionInfo Bind() { ExecutionInfo retVal = new ExecutionInfo(); // IQueryable impl if (this.Expression != null) { Dictionary <Expression, Expression> normalizerRewrites = new Dictionary <Expression, Expression>(ReferenceEqualityComparer <Expression> .Instance); // Step 1. Evaluate any local evaluatable expressions ( lambdas etc) Expression partialEvaluatedExpression = Evaluator.PartialEval(this.Expression); // Step 2. Normalize expression, replace String Comparisons etc. Expression normalizedExpression = ExpressionNormalizer.Normalize(partialEvaluatedExpression, normalizerRewrites); // Step 3. Bind Expression, Analyze predicates and create query option expressions. End result is a single ResourceSetExpression Expression boundExpression = ResourceBinder.Bind(normalizedExpression); // Step 4. Parse the Bound expression into sub components, i.e. take count, filter, select columns, request options, opcontext, etc. ExpressionParser parser = new ExpressionParser(); parser.Translate(boundExpression); // Step 5. Store query components & params this.TakeCount = parser.TakeCount; this.FilterString = parser.FilterString; this.SelectColumns = parser.SelectColumns; retVal.RequestOptions = parser.RequestOptions; retVal.OperationContext = parser.OperationContext; // Step 6. If projection & no resolver then generate a resolver to perform the projection if (parser.Resolver == null) { if (parser.Projection != null && parser.Projection.Selector != ProjectionQueryOptionExpression.DefaultLambda) { Type intermediateType = parser.Projection.Selector.Parameters[0].Type; // Convert Expression to take type object as input to allow for direct invocation. ParameterExpression paramExpr = Expression.Parameter(typeof(object)); Func <object, TElement> projectorFunc = Expression.Lambda <Func <object, TElement> >( Expression.Invoke(parser.Projection.Selector, Expression.Convert(paramExpr, intermediateType)), paramExpr).Compile(); // Generate a resolver to do the projection. retVal.Resolver = (pk, rk, ts, props, etag) => { // Parse to intermediate type ITableEntity intermediateObject = (ITableEntity)EntityUtilities.InstantiateEntityFromType(intermediateType); intermediateObject.PartitionKey = pk; intermediateObject.RowKey = rk; intermediateObject.Timestamp = ts; intermediateObject.ReadEntity(props, parser.OperationContext); intermediateObject.ETag = etag; // Invoke lambda expression return(projectorFunc(intermediateObject)); }; } else { // No op - No resolver or projection specified. } } else { retVal.Resolver = (EntityResolver <TElement>)parser.Resolver.Value; } } retVal.RequestOptions = TableRequestOptions.ApplyDefaults(retVal.RequestOptions, this.queryProvider.Table.ServiceClient); retVal.OperationContext = retVal.OperationContext ?? new OperationContext(); return(retVal); }
/// <summary> /// Rebinds a conditional that performs a null check on an entity. /// </summary> /// <param name="conditional">Conditional expression.</param> /// <param name="nullCheck">Results of null check analysis.</param> /// <returns>The rebound expression.</returns> /// <remarks> /// Do a rewrite to avoid creating a type in the null check: /// a.b == null ? null : [a.b]-based expression /// becomes /// ProjectionIsNull(a.b) ? null : [a.b]-based expression /// </remarks> private Expression RebindConditionalNullCheck(ConditionalExpression conditional, ResourceBinder.PatternRules.MatchNullCheckResult nullCheck) { Debug.Assert(conditional != null, "conditional != null"); Debug.Assert(nullCheck.Match, "nullCheck.Match -- otherwise no reason to call this rebind method"); Expression testToNullForProjection = this.Visit(nullCheck.TestToNullExpression); Expression assignForProjection = this.Visit(nullCheck.AssignExpression); ExpressionAnnotation testToNullAnnotation; if (!this.annotations.TryGetValue(testToNullForProjection, out testToNullAnnotation)) { return base.VisitConditional(conditional); } ProjectionPathSegment testToNullSegment = testToNullAnnotation.Segment; Expression testToNullThroughMethod = this.CallCheckValueForPathIsNull( testToNullSegment.StartPath.RootEntry, testToNullSegment.StartPath.ExpectedRootType, testToNullSegment.StartPath); Expression test = testToNullThroughMethod; Expression iftrue = Expression.Constant(null, assignForProjection.Type); Expression iffalse = assignForProjection; Expression result = Expression.Condition(test, iftrue, iffalse); return result; }
private Expression RebindConditionalNullCheck(ConditionalExpression conditional, ResourceBinder.PatternRules.MatchNullCheckResult nullCheck) { ExpressionAnnotation annotation; Expression key = this.Visit(nullCheck.TestToNullExpression); Expression expression2 = this.Visit(nullCheck.AssignExpression); if (!this.annotations.TryGetValue(key, out annotation)) { return base.VisitConditional(conditional); } ProjectionPathSegment segment = annotation.Segment; Expression test = this.CallCheckValueForPathIsNull(segment.StartPath.RootEntry, segment.StartPath.ExpectedRootType, segment.StartPath); Expression ifTrue = Expression.Constant(null, expression2.Type); Expression ifFalse = expression2; return Expression.Condition(test, ifTrue, ifFalse); }