/mcs/class/System.Data.Services.Client/Client/System/Data/Services/Client/ALinq/ResourceBinder.cs
C# | 1945 lines | 1634 code | 301 blank | 10 comment | 451 complexity | 074d608ea53fbe65428319a63a39b945 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
Large files files are truncated, but you can click here to view the full file
- //Copyright 2010 Microsoft Corporation
- //
- //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
- //You may obtain a copy of the License at
- //
- //http://www.apache.org/licenses/LICENSE-2.0
- //
- //Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
- //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- //See the License for the specific language governing permissions and limitations under the License.
-
-
- namespace System.Data.Services.Client
- {
- #region Namespaces.
-
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
-
- #endregion Namespaces.
-
- internal class ResourceBinder : DataServiceExpressionVisitor
- {
- internal static Expression Bind(Expression e)
- {
- Debug.Assert(e != null, "e != null");
-
- ResourceBinder rb = new ResourceBinder();
- Expression boundExpression = rb.Visit(e);
- VerifyKeyPredicates(boundExpression);
- VerifyNotSelectManyProjection(boundExpression);
- return boundExpression;
- }
-
- internal static bool IsMissingKeyPredicates(Expression expression)
- {
- ResourceExpression re = expression as ResourceExpression;
- if (re != null)
- {
- if (IsMissingKeyPredicates(re.Source))
- {
- return true;
- }
-
- if (re.Source != null)
- {
- ResourceSetExpression rse = re.Source as ResourceSetExpression;
- if ((rse != null) && !rse.HasKeyPredicate)
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- internal static void VerifyKeyPredicates(Expression e)
- {
- if (IsMissingKeyPredicates(e))
- {
- throw new NotSupportedException(Strings.ALinq_CantNavigateWithoutKeyPredicate);
- }
- }
-
- internal static void VerifyNotSelectManyProjection(Expression expression)
- {
- Debug.Assert(expression != null, "expression != null");
-
- ResourceSetExpression resourceSet = expression as ResourceSetExpression;
- if (resourceSet != null)
- {
- ProjectionQueryOptionExpression projection = resourceSet.Projection;
- if (projection != null)
- {
- Debug.Assert(projection.Selector != null, "projection.Selector != null -- otherwise incorrectly constructed");
- MethodCallExpression call = StripTo<MethodCallExpression>(projection.Selector.Body);
- if (call != null && call.Method.Name == "SelectMany")
- {
- throw new NotSupportedException(Strings.ALinq_UnsupportedExpression(call));
- }
- }
- else if (resourceSet.HasTransparentScope)
- {
- throw new NotSupportedException(Strings.ALinq_UnsupportedExpression(resourceSet));
- }
- }
- }
-
- private static Expression AnalyzePredicate(MethodCallExpression mce)
- {
- Debug.Assert(mce != null, "mce != null -- caller couldn't have know the expression kind otherwise");
- Debug.Assert(mce.Method.Name == "Where", "mce.Method.Name == 'Where' -- otherwise this isn't a predicate");
-
- ResourceSetExpression input;
- LambdaExpression le;
- if (!TryGetResourceSetMethodArguments(mce, out input, out le))
- {
- ValidationRules.RequireNonSingleton(mce.Arguments[0]);
- return mce;
- }
-
-
- List<Expression> conjuncts = new List<Expression>();
- AddConjuncts(le.Body, conjuncts);
-
- Dictionary<ResourceSetExpression, List<Expression>> predicatesByTarget = new Dictionary<ResourceSetExpression, List<Expression>>(ReferenceEqualityComparer<ResourceSetExpression>.Instance);
- List<ResourceExpression> referencedInputs = new List<ResourceExpression>();
- foreach (Expression e in conjuncts)
- {
- Expression reboundPredicate = InputBinder.Bind(e, input, le.Parameters[0], referencedInputs);
- if (referencedInputs.Count > 1)
- {
- return mce;
- }
-
- ResourceSetExpression boundTarget = (referencedInputs.Count == 0 ? input : referencedInputs[0] as ResourceSetExpression);
- if (boundTarget == null)
- {
- return mce;
- }
-
- List<Expression> targetPredicates = null;
- if (!predicatesByTarget.TryGetValue(boundTarget, out targetPredicates))
- {
- targetPredicates = new List<Expression>();
- predicatesByTarget[boundTarget] = targetPredicates;
- }
-
- targetPredicates.Add(reboundPredicate);
- referencedInputs.Clear();
- }
-
- conjuncts = null;
- List<Expression> inputPredicates;
- if (predicatesByTarget.TryGetValue(input, out inputPredicates))
- {
- predicatesByTarget.Remove(input);
- }
- else
- {
- inputPredicates = null;
- }
-
- foreach (KeyValuePair<ResourceSetExpression, List<Expression>> predicates in predicatesByTarget)
- {
- ResourceSetExpression target = predicates.Key;
- List<Expression> clauses = predicates.Value;
-
- Dictionary<PropertyInfo, ConstantExpression> keyValues;
- if (!ExtractKeyPredicate(target, clauses, out keyValues) ||
- clauses.Count > 0)
- {
- return mce;
- }
-
- Debug.Assert(!target.HasQueryOptions, "Navigation source had query options?");
-
- SetKeyPredicate(target, keyValues);
- }
-
- if (inputPredicates != null)
- {
- Dictionary<PropertyInfo, ConstantExpression> inputKeyValues;
- if (ExtractKeyPredicate(input, inputPredicates, out inputKeyValues))
- {
- if (input.HasSequenceQueryOptions)
- {
- Expression predicateFilter = BuildKeyPredicateFilter(input.CreateReference(), inputKeyValues);
- inputPredicates.Add(predicateFilter);
- }
- else
- {
- SetKeyPredicate(input, inputKeyValues);
- }
- }
-
- if (inputPredicates.Count > 0)
- {
- if (input.KeyPredicate != null)
- {
- Expression predicateFilter = BuildKeyPredicateFilter(input.CreateReference(), input.KeyPredicate);
- inputPredicates.Add(predicateFilter);
- input.KeyPredicate = null;
- }
-
- int start;
- Expression newFilter;
- if (input.Filter != null)
- {
- start = 0;
- newFilter = input.Filter.Predicate;
- }
- else
- {
- start = 1;
- newFilter = inputPredicates[0];
- }
-
- for (int idx = start; idx < inputPredicates.Count; idx++)
- {
- newFilter = Expression.And(newFilter, inputPredicates[idx]);
- }
-
- AddSequenceQueryOption(input, new FilterQueryOptionExpression(mce.Method.ReturnType, newFilter));
- }
- }
-
- return input; }
-
- private static void SetKeyPredicate(ResourceSetExpression rse, Dictionary<PropertyInfo, ConstantExpression> keyValues)
- {
- Debug.Assert(rse != null, "rse != null");
- Debug.Assert(keyValues != null, "keyValues != null");
-
- if (rse.KeyPredicate == null)
- {
- rse.KeyPredicate = new Dictionary<PropertyInfo, ConstantExpression>(EqualityComparer<PropertyInfo>.Default);
- }
-
- foreach(var g in keyValues)
- {
- if (rse.KeyPredicate.Keys.Contains(g.Key))
- {
- throw Error.NotSupported(Strings.ALinq_CanOnlyApplyOneKeyPredicate);
- }
-
- rse.KeyPredicate.Add(g.Key, g.Value);
- }
- }
-
- private static bool CollectionContentsEqual<T>(ICollection<T> left, ICollection<T> right, IEqualityComparer<T> comparer) where T : class
- {
- Debug.Assert(left != null, "left != null");
- Debug.Assert(right != null, "right != null");
- Debug.Assert(comparer != null, "comparer != null");
-
- if (left.Count != right.Count)
- {
- return false;
- }
-
- if (left.Count == 1)
- {
- return comparer.Equals(left.First(), right.First());
- }
- else
- {
- #if ASTORIA_LIGHT
- HashSet<T> leftElements = new HashSet<T>(comparer);
- foreach (var l in left)
- {
- leftElements.Add(l);
- }
- #else
- HashSet<T> leftElements = new HashSet<T>(left, comparer);
- #endif
- foreach (T rightElement in right)
- {
- if (!leftElements.Contains(rightElement))
- {
- return false;
- }
- }
-
- return true;
- }
- }
-
- private static bool ExtractKeyPredicate(
- ResourceSetExpression target,
- List<Expression> predicates,
- out Dictionary<PropertyInfo, ConstantExpression> keyValues)
- {
- Debug.Assert(target != null, "target != null");
- Debug.Assert(predicates != null, "predicates != null");
-
- keyValues = null;
- List<Expression> nonKeyPredicates = null;
-
- foreach (Expression predicate in predicates)
- {
- PropertyInfo property;
- ConstantExpression constantValue;
- if (PatternRules.MatchKeyComparison(predicate, out property, out constantValue))
- {
- if (keyValues == null)
- {
- keyValues = new Dictionary<PropertyInfo, ConstantExpression>(EqualityComparer<PropertyInfo>.Default);
- }
- else if (keyValues.ContainsKey(property))
- {
- throw Error.NotSupported(Strings.ALinq_CanOnlyApplyOneKeyPredicate);
- }
-
- keyValues.Add(property, constantValue);
- }
- else
- {
- if (nonKeyPredicates == null)
- {
- nonKeyPredicates = new List<Expression>();
- }
-
- nonKeyPredicates.Add(predicate);
- }
- }
-
- Debug.Assert(keyValues != null || nonKeyPredicates != null, "No key predicates or non-key predicates found?");
- if (keyValues != null)
- {
- var properties = PatternRules.GetKeyProperties(target.CreateReference().Type);
- if (!CollectionContentsEqual(properties, keyValues.Keys, PropertyInfoEqualityComparer.Instance))
- {
- keyValues = null;
- return false;
- }
- }
-
- if (keyValues != null)
- {
- predicates.Clear();
-
- if (nonKeyPredicates != null)
- {
- predicates.AddRange(nonKeyPredicates);
- }
- }
-
- return keyValues != null;
- }
-
- private static Expression BuildKeyPredicateFilter(InputReferenceExpression input, Dictionary<PropertyInfo, ConstantExpression> keyValuesDictionary)
- {
- Debug.Assert(input != null, "input != null");
- Debug.Assert(keyValuesDictionary != null, "keyValuesDictionary != null");
- Debug.Assert(keyValuesDictionary.Count > 0, "At least one key property is required in a key predicate");
-
- Expression retExpr = null;
- foreach (KeyValuePair<PropertyInfo, ConstantExpression> keyValue in keyValuesDictionary)
- {
- Expression clause = Expression.Equal(Expression.Property(input, keyValue.Key), keyValue.Value);
- if (retExpr == null)
- {
- retExpr = clause;
- }
- else
- {
- retExpr = Expression.And(retExpr, clause);
- }
- }
-
- return retExpr;
- }
-
- private static void AddConjuncts(Expression e, List<Expression> conjuncts)
- {
- Debug.Assert(conjuncts != null, "conjuncts != null");
- if (PatternRules.MatchAnd(e))
- {
- BinaryExpression be = (BinaryExpression)e;
- AddConjuncts(be.Left, conjuncts);
- AddConjuncts(be.Right, conjuncts);
- }
- else
- {
- conjuncts.Add(e);
- }
- }
-
- internal bool AnalyzeProjection(MethodCallExpression mce, SequenceMethod sequenceMethod, out Expression e)
- {
- Debug.Assert(mce != null, "mce != null");
- Debug.Assert(
- sequenceMethod == SequenceMethod.Select || sequenceMethod == SequenceMethod.SelectManyResultSelector,
- "sequenceMethod == SequenceMethod.Select(ManyResultSelector)");
-
- e = mce;
-
- bool matchMembers = sequenceMethod == SequenceMethod.SelectManyResultSelector;
- ResourceExpression source = this.Visit(mce.Arguments[0]) as ResourceExpression;
- if (source == null)
- {
- return false;
- }
-
- if (sequenceMethod == SequenceMethod.SelectManyResultSelector)
- {
- Expression collectionSelector = mce.Arguments[1];
- if (!PatternRules.MatchParameterMemberAccess(collectionSelector))
- {
- return false;
- }
-
- Expression resultSelector = mce.Arguments[2];
- LambdaExpression resultLambda;
- if (!PatternRules.MatchDoubleArgumentLambda(resultSelector, out resultLambda))
- {
- return false;
- }
-
- if (ExpressionPresenceVisitor.IsExpressionPresent(resultLambda.Parameters[0], resultLambda.Body))
- {
- return false;
- }
-
- List<ResourceExpression> referencedExpressions = new List<ResourceExpression>();
- LambdaExpression collectionLambda = StripTo<LambdaExpression>(collectionSelector);
- Expression collectorReference = InputBinder.Bind(collectionLambda.Body, source, collectionLambda.Parameters[0], referencedExpressions);
- collectorReference = StripCastMethodCalls(collectorReference);
- MemberExpression navigationMember;
- if (!PatternRules.MatchPropertyProjectionSet(source, collectorReference, out navigationMember))
- {
- return false;
- }
-
- collectorReference = navigationMember;
-
- ResourceExpression resultSelectorSource = CreateResourceSetExpression(mce.Method.ReturnType, source, collectorReference, TypeSystem.GetElementType(collectorReference.Type));
-
- if (!PatternRules.MatchMemberInitExpressionWithDefaultConstructor(resultSelectorSource, resultLambda) &&
- !PatternRules.MatchNewExpression(resultSelectorSource, resultLambda))
- {
- return false;
- }
-
- #if ASTORIA_LIGHT
- resultLambda = ExpressionHelpers.CreateLambda(resultLambda.Body, new ParameterExpression[] { resultLambda.Parameters[1] });
- #else
- resultLambda = Expression.Lambda(resultLambda.Body, new ParameterExpression[] { resultLambda.Parameters[1] });
- #endif
-
- ResourceExpression resultWithProjection = resultSelectorSource.CreateCloneWithNewType(mce.Type);
- bool isProjection;
- try
- {
- isProjection = ProjectionAnalyzer.Analyze(resultLambda, resultWithProjection, false);
- }
- catch (NotSupportedException)
- {
- isProjection = false;
- }
-
- if (!isProjection)
- {
- return false;
- }
-
- e = resultWithProjection;
- ValidationRules.RequireCanProject(resultSelectorSource);
- }
- else
- {
- LambdaExpression lambda;
- if (!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out lambda))
- {
- return false;
- }
-
- lambda = ProjectionRewriter.TryToRewrite(lambda, source.ResourceType);
-
- ResourceExpression re = source.CreateCloneWithNewType(mce.Type);
-
- if (!ProjectionAnalyzer.Analyze(lambda, re, matchMembers))
- {
- return false;
- }
-
- ValidationRules.RequireCanProject(source);
- e = re;
- }
-
- return true;
- }
-
- internal static Expression AnalyzeNavigation(MethodCallExpression mce)
- {
- Debug.Assert(mce != null, "mce != null");
- Expression input = mce.Arguments[0];
- LambdaExpression le;
- ResourceExpression navSource;
- Expression boundProjection;
- MemberExpression navigationMember;
-
- if (!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out le))
- {
- return mce;
- }
- else if (PatternRules.MatchIdentitySelector(le))
- {
- return input;
- }
- else if (PatternRules.MatchTransparentIdentitySelector(input, le))
- {
- return RemoveTransparentScope(mce.Method.ReturnType, (ResourceSetExpression)input);
- }
- else if (IsValidNavigationSource(input, out navSource) &&
- TryBindToInput(navSource, le, out boundProjection) &&
- PatternRules.MatchPropertyProjectionSingleton(navSource, boundProjection, out navigationMember))
- {
- boundProjection = navigationMember;
- return CreateNavigationPropertySingletonExpression(mce.Method.ReturnType, navSource, boundProjection);
- }
-
- return mce;
- }
-
- private static bool IsValidNavigationSource(Expression input, out ResourceExpression sourceExpression)
- {
- ValidationRules.RequireCanNavigate(input);
- sourceExpression = input as ResourceExpression;
- return sourceExpression != null;
- }
-
- internal static Expression AnalyzeSelectMany(MethodCallExpression mce)
- {
- Debug.Assert(mce != null, "mce != null");
-
- if (mce.Arguments.Count != 2 && mce.Arguments.Count != 3)
- {
- return mce;
- }
-
- ResourceExpression input;
- if (!IsValidNavigationSource(mce.Arguments[0], out input))
- {
- return mce;
- }
-
- LambdaExpression collectorSelector;
- if (!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out collectorSelector))
- {
- return mce;
- }
-
- List<ResourceExpression> referencedInputs = new List<ResourceExpression>();
- Expression navPropRef = InputBinder.Bind(collectorSelector.Body, input, collectorSelector.Parameters[0], referencedInputs);
- Type resourceType = TypeSystem.GetElementType(navPropRef.Type);
-
- navPropRef = StripCastMethodCalls(navPropRef);
-
- ResourceSetExpression rse = null;
- MemberExpression navigationMember;
- if (PatternRules.MatchPropertyProjectionSet(input, navPropRef, out navigationMember))
- {
- navPropRef = navigationMember;
- rse = CreateResourceSetExpression(mce.Method.ReturnType, input, navPropRef, resourceType);
- }
- else
- {
- return mce;
- }
-
- if (mce.Arguments.Count == 3)
- {
- return AnalyzeSelectManySelector(mce, rse);
- }
- else
- {
- return rse;
- }
- }
-
- private static Expression AnalyzeSelectManySelector(MethodCallExpression selectManyCall, ResourceSetExpression sourceResourceSet)
- {
- Debug.Assert(selectManyCall != null, "selectManyCall != null");
-
- LambdaExpression selector = StripTo<LambdaExpression>(selectManyCall.Arguments[2]);
-
- Expression result;
- ResourceSetExpression.TransparentAccessors transparentScope;
- if (PatternRules.MatchTransparentScopeSelector(sourceResourceSet, selector, out transparentScope))
- {
- sourceResourceSet.TransparentScope = transparentScope;
- result = sourceResourceSet;
- }
- else if (PatternRules.MatchIdentityProjectionResultSelector(selector))
- {
- result = sourceResourceSet;
- }
- else if (PatternRules.MatchMemberInitExpressionWithDefaultConstructor(sourceResourceSet, selector) || PatternRules.MatchNewExpression(sourceResourceSet, selector))
- {
- #if ASTORIA_LIGHT
- selector = ExpressionHelpers.CreateLambda(selector.Body, new ParameterExpression[] { selector.Parameters[1] });
- #else
- selector = Expression.Lambda(selector.Body, new ParameterExpression[] { selector.Parameters[1] });
- #endif
- if (!ProjectionAnalyzer.Analyze(selector, sourceResourceSet, false))
- {
- result = selectManyCall;
- }
- else
- {
- result = sourceResourceSet;
- }
- }
- else
- {
- result = selectManyCall;
- }
-
- return result;
- }
-
- internal static Expression ApplyOrdering(MethodCallExpression mce, ResourceSetExpression input, Expression selector, bool descending, bool thenBy)
- {
- List<OrderByQueryOptionExpression.Selector> selectors;
- if (!thenBy)
- {
- selectors = new List<OrderByQueryOptionExpression.Selector>();
- AddSequenceQueryOption(input, new OrderByQueryOptionExpression(mce.Type, selectors));
- }
- else
- {
- Debug.Assert(input.OrderBy != null, "input.OrderBy != null");
- selectors = input.OrderBy.Selectors;
- }
-
- selectors.Add(new OrderByQueryOptionExpression.Selector(selector, descending));
-
- return input;
- }
-
- #if !ASTORIA_LIGHT
- private static Expression LimitCardinality(MethodCallExpression mce, int maxCardinality)
- {
- Debug.Assert(mce != null, "mce != null");
- Debug.Assert(maxCardinality > 0, "Cardinality must be at least 1");
-
- if (mce.Arguments.Count != 1)
- {
- return mce;
- }
-
- ResourceSetExpression rse = mce.Arguments[0] as ResourceSetExpression;
- if (rse != null)
- {
- if (!rse.HasKeyPredicate && (ResourceExpressionType)rse.NodeType != ResourceExpressionType.ResourceNavigationProperty)
- {
- if (rse.Take == null || (int)rse.Take.TakeAmount.Value > maxCardinality)
- {
- AddSequenceQueryOption(rse, new TakeQueryOptionExpression(mce.Type, Expression.Constant(maxCardinality)));
- }
- }
- return mce.Arguments[0];
- }
- else if (mce.Arguments[0] is NavigationPropertySingletonExpression)
- {
- return mce.Arguments[0];
- }
-
- return mce;
- }
-
- #endif
-
- private static Expression AnalyzeCast(MethodCallExpression mce)
- {
- ResourceExpression re = mce.Arguments[0] as ResourceExpression;
- if (re != null)
- {
- return re.CreateCloneWithNewType(mce.Method.ReturnType);
- }
-
- return mce;
- }
-
- private static Expression StripConvert(Expression e)
- {
- UnaryExpression ue = e as UnaryExpression;
-
- if (ue != null &&
- ue.NodeType == ExpressionType.Convert &&
- ue.Type.IsGenericType &&
- (ue.Type.GetGenericTypeDefinition() == typeof(DataServiceQuery<>) ||
- ue.Type.GetGenericTypeDefinition() == typeof(DataServiceQuery<>.DataServiceOrderedQuery)))
- {
- e = ue.Operand;
- ResourceExpression re = e as ResourceExpression;
- if (re != null)
- {
- e = re.CreateCloneWithNewType(ue.Type);
- }
- }
-
- return e;
- }
-
- private static Expression AnalyzeExpand(MethodCallExpression mce)
- {
- Expression obj = StripConvert(mce.Object);
- ResourceExpression re = obj as ResourceExpression;
- if (re == null)
- {
- return mce;
- }
-
- ValidationRules.RequireCanExpand(re);
- ConstantExpression ce = StripTo<ConstantExpression>(mce.Arguments[0]);
- string path = (string)ce.Value;
- if (!re.ExpandPaths.Contains(path))
- {
- re.ExpandPaths.Add(path);
- }
-
- return re;
- }
-
- private static Expression AnalyzeAddCustomQueryOption(MethodCallExpression mce)
- {
- Expression obj = StripConvert(mce.Object);
- ResourceExpression re = obj as ResourceExpression;
- if (re == null)
- {
- return mce;
- }
-
- ValidationRules.RequireCanAddCustomQueryOption(re);
-
- ConstantExpression name = StripTo<ConstantExpression>(mce.Arguments[0]);
- ConstantExpression value = StripTo<ConstantExpression>(mce.Arguments[1]);
-
- if (((string)name.Value).Trim() == UriHelper.DOLLARSIGN + UriHelper.OPTIONEXPAND)
- {
- ValidationRules.RequireCanExpand(re);
- re.ExpandPaths = re.ExpandPaths.Union(new string[] { (string) value.Value }, StringComparer.Ordinal).ToList();
- }
- else
- {
- ValidationRules.RequireLegalCustomQueryOption(mce.Arguments[0], re);
- re.CustomQueryOptions.Add(name, value);
- }
-
- return re;
- }
-
- private static Expression AnalyzeAddCountOption(MethodCallExpression mce, CountOption countOption)
- {
- Expression obj = StripConvert(mce.Object);
- ResourceExpression re = obj as ResourceExpression;
- if (re == null)
- {
- return mce;
- }
-
- ValidationRules.RequireCanAddCount(re);
- ValidationRules.RequireNonSingleton(re);
- re.CountOption = countOption;
-
- return re;
- }
-
- private static ResourceSetExpression CreateResourceSetExpression(Type type, ResourceExpression source, Expression memberExpression, Type resourceType)
- {
- Debug.Assert(type != null, "type != null");
- Debug.Assert(source != null, "source != null");
- Debug.Assert(memberExpression != null, "memberExpression != null");
- Debug.Assert(resourceType != null, "resourceType != null");
-
- Type elementType = TypeSystem.GetElementType(type);
- Debug.Assert(elementType != null, "elementType != null -- otherwise the set isn't going to act like a collection");
- Type expressionType = typeof(IOrderedQueryable<>).MakeGenericType(elementType);
-
- ResourceSetExpression newResource = new ResourceSetExpression(expressionType, source, memberExpression, resourceType, source.ExpandPaths.ToList(), source.CountOption, source.CustomQueryOptions.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), null);
- source.ExpandPaths.Clear();
- source.CountOption = CountOption.None;
- source.CustomQueryOptions.Clear();
- return newResource;
- }
-
- private static NavigationPropertySingletonExpression CreateNavigationPropertySingletonExpression(Type type, ResourceExpression source, Expression memberExpression)
- {
- NavigationPropertySingletonExpression newResource = new NavigationPropertySingletonExpression(type, source, memberExpression, memberExpression.Type, source.ExpandPaths.ToList(), source.CountOption, source.CustomQueryOptions.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), null);
- source.ExpandPaths.Clear();
- source.CountOption = CountOption.None;
- source.CustomQueryOptions.Clear();
- return newResource;
- }
-
- private static ResourceSetExpression RemoveTransparentScope(Type expectedResultType, ResourceSetExpression input)
- {
- ResourceSetExpression newResource = new ResourceSetExpression(expectedResultType, input.Source, input.MemberExpression, input.ResourceType, input.ExpandPaths, input.CountOption, input.CustomQueryOptions, input.Projection);
-
- newResource.KeyPredicate = input.KeyPredicate;
- foreach (QueryOptionExpression queryOption in input.SequenceQueryOptions)
- {
- newResource.AddSequenceQueryOption(queryOption);
- }
-
- newResource.OverrideInputReference(input);
-
- return newResource;
- }
-
- internal static Expression StripConvertToAssignable(Expression e)
- {
- Debug.Assert(e != null, "e != null");
-
- Expression result;
- UnaryExpression unary = e as UnaryExpression;
- if (unary != null && PatternRules.MatchConvertToAssignable(unary))
- {
- result = unary.Operand;
- }
- else
- {
- result = e;
- }
-
- return result;
- }
-
- internal static T StripTo<T>(Expression expression) where T : Expression
- {
- Debug.Assert(expression != null, "expression != null");
-
- Expression result;
- do
- {
- result = expression;
- expression = expression.NodeType == ExpressionType.Quote ? ((UnaryExpression)expression).Operand : expression;
- expression = StripConvertToAssignable(expression);
- }
- while (result != expression);
-
- return result as T;
- }
-
- internal override Expression VisitResourceSetExpression(ResourceSetExpression rse)
- {
- Debug.Assert(rse != null, "rse != null");
-
- if ((ResourceExpressionType)rse.NodeType == ResourceExpressionType.RootResourceSet)
- {
-
- return new ResourceSetExpression(rse.Type, rse.Source, rse.MemberExpression, rse.ResourceType, null, CountOption.None, null, null);
- }
-
- return rse;
- }
-
- private static bool TryGetResourceSetMethodArguments(MethodCallExpression mce, out ResourceSetExpression input, out LambdaExpression lambda)
- {
- input = null;
- lambda = null;
-
- input = mce.Arguments[0] as ResourceSetExpression;
- if (input != null &&
- PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out lambda))
- {
- return true;
- }
-
- return false;
- }
-
- private static bool TryBindToInput(ResourceExpression input, LambdaExpression le, out Expression bound)
- {
- List<ResourceExpression> referencedInputs = new List<ResourceExpression>();
- bound = InputBinder.Bind(le.Body, input, le.Parameters[0], referencedInputs);
- if (referencedInputs.Count > 1 || (referencedInputs.Count == 1 && referencedInputs[0] != input))
- {
- bound = null;
- }
-
- return bound != null;
- }
-
- private static Expression AnalyzeResourceSetMethod(MethodCallExpression mce, Func<MethodCallExpression, ResourceSetExpression, Expression, Expression> sequenceMethodAnalyzer)
- {
- ResourceSetExpression input;
- LambdaExpression le;
- if (!TryGetResourceSetMethodArguments(mce, out input, out le))
- {
- return mce;
- }
-
- Expression lambdaBody;
- if (!TryBindToInput(input, le, out lambdaBody))
- {
- return mce;
- }
-
- return sequenceMethodAnalyzer(mce, input, lambdaBody);
- }
-
- private static Expression AnalyzeResourceSetConstantMethod(MethodCallExpression mce, Func<MethodCallExpression, ResourceExpression, ConstantExpression, Expression> constantMethodAnalyzer)
- {
- ResourceExpression input = (ResourceExpression)mce.Arguments[0];
- ConstantExpression constantArg = StripTo<ConstantExpression>(mce.Arguments[1]);
- if (null == constantArg)
- {
- return mce;
- }
-
- return constantMethodAnalyzer(mce, input, constantArg);
- }
-
- private static Expression AnalyzeCountMethod(MethodCallExpression mce)
- {
- ResourceExpression re = (ResourceExpression)mce.Arguments[0];
- if (re == null)
- {
- return mce;
- }
-
- ValidationRules.RequireCanAddCount(re);
- ValidationRules.RequireNonSingleton(re);
- re.CountOption = CountOption.ValueOnly;
-
- return re;
- }
-
- private static void AddSequenceQueryOption(ResourceExpression target, QueryOptionExpression qoe)
- {
- ValidationRules.RequireNonSingleton(target);
- ResourceSetExpression rse = (ResourceSetExpression)target;
-
- switch (qoe.NodeType)
- {
- case (ExpressionType)ResourceExpressionType.FilterQueryOption:
- if (rse.Skip != null)
- {
- throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "skip"));
- }
- else if (rse.Take != null)
- {
- throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "top"));
- }
- else if (rse.Projection != null)
- {
- throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "select"));
- }
- break;
- case (ExpressionType)ResourceExpressionType.OrderByQueryOption:
- if (rse.Skip != null)
- {
- throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "skip"));
- }
- else if (rse.Take != null)
- {
- throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "top"));
- }
- else if (rse.Projection != null)
- {
- throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "select"));
- }
- break;
- case (ExpressionType)ResourceExpressionType.SkipQueryOption:
- if (rse.Take != null)
- {
- throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("skip", "top"));
- }
- break;
- default:
- break;
- }
-
- rse.AddSequenceQueryOption(qoe);
- }
-
- internal override Expression VisitBinary(BinaryExpression b)
- {
- Expression e = base.VisitBinary(b);
- if (PatternRules.MatchStringAddition(e))
- {
- BinaryExpression be = StripTo<BinaryExpression>(e);
- MethodInfo mi = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string)});
- return Expression.Call(mi, new Expression[] {be.Left, be.Right});
- }
-
- return e;
- }
-
- internal override Expression VisitMemberAccess(MemberExpression m)
- {
- Expression e = base.VisitMemberAccess(m);
- MemberExpression me = StripTo<MemberExpression>(e);
- PropertyInfo pi;
- MethodInfo mi;
- if (me != null &&
- PatternRules.MatchNonPrivateReadableProperty(me, out pi) &&
- TypeSystem.TryGetPropertyAsMethod(pi, out mi))
- {
- return Expression.Call(me.Expression, mi);
- }
-
- return e;
- }
-
- internal override Expression VisitMethodCall(MethodCallExpression mce)
- {
- Expression e;
-
- SequenceMethod sequenceMethod;
- if (ReflectionUtil.TryIdentifySequenceMethod(mce.Method, out sequenceMethod))
- {
- if (sequenceMethod == SequenceMethod.Select ||
- sequenceMethod == SequenceMethod.SelectManyResultSelector)
- {
- if (this.AnalyzeProjection(mce, sequenceMethod, out e))
- {
- return e;
- }
- }
- }
-
- e = base.VisitMethodCall(mce);
- mce = e as MethodCallExpression;
-
- if (mce != null)
- {
- if (ReflectionUtil.TryIdentifySequenceMethod(mce.Method, out sequenceMethod))
- {
- switch (sequenceMethod)
- {
- case SequenceMethod.Where:
- return AnalyzePredicate(mce);
- case SequenceMethod.Select:
- return AnalyzeNavigation(mce);
- case SequenceMethod.SelectMany:
- case SequenceMethod.SelectManyResultSelector:
- {
- Expression result = AnalyzeSelectMany(mce);
- return result;
- }
-
- case SequenceMethod.Take:
- return AnalyzeResourceSetConstantMethod(mce, (callExp, resource, takeCount) => { AddSequenceQueryOption(resource, new TakeQueryOptionExpression(callExp.Type, takeCount)); return resource; });
- case SequenceMethod.Skip:
- return AnalyzeResourceSetConstantMethod(mce, (callExp, resource, skipCount) => { AddSequenceQueryOption(resource, new SkipQueryOptionExpression(callExp.Type, skipCount)); return resource; });
- case SequenceMethod.OrderBy:
- return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector,false, false));
- case SequenceMethod.ThenBy:
- return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, false,true));
- case SequenceMethod.OrderByDescending:
- return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, true, false));
- case SequenceMethod.ThenByDescending:
- return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector,true, true));
- #if !ASTORIA_LIGHT
- case SequenceMethod.First:
- case SequenceMethod.FirstOrDefault:
- return LimitCardinality(mce, 1);
- case SequenceMethod.Single:
- case SequenceMethod.SingleOrDefault:
- return LimitCardinality(mce, 2);
- #endif
- case SequenceMethod.Cast:
- return AnalyzeCast(mce);
- case SequenceMethod.LongCount:
- case SequenceMethod.Count:
- return AnalyzeCountMethod(mce);
- default:
- throw Error.MethodNotSupported(mce);
- }
- }
- else if (mce.Method.DeclaringType.IsGenericType &&
- mce.Method.DeclaringType.GetGenericTypeDefinition() == typeof(DataServiceQuery<>))
- {
- Type t = typeof(DataServiceQuery<>).MakeGenericType(mce.Method.DeclaringType.GetGenericArguments()[0]);
-
- if (mce.Method == t.GetMethod("Expand", new Type[] { typeof(string) }))
- {
- return AnalyzeExpand(mce);
- }
- else if (mce.Method == t.GetMethod("AddQueryOption", new Type[] { typeof(string), typeof(object) }))
- {
- return AnalyzeAddCustomQueryOption(mce);
- }
- else if (mce.Method == t.GetMethod("IncludeTotalCount"))
- {
- return AnalyzeAddCountOption(mce, CountOption.InlineAll);
- }
- else
- {
- throw Error.MethodNotSupported(mce);
- }
- }
- return mce;
- }
-
- return e;
- }
-
-
- private static Expression StripCastMethodCalls(Expression expression)
- {
- Debug.Assert(expression != null, "expression != null");
-
- MethodCallExpression call = StripTo<MethodCallExpression>(expression);
- while (call != null && ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.Cast))
- {
- expression = call.Arguments[0];
- call = StripTo<MethodCallExpression>(expression);
- }
-
- return expression;
- }
-
-
-
- internal static class PatternRules
- {
- internal static bool MatchConvertToAssignable(UnaryExpression expression)
- {
- Debug.Assert(expression != null, "expression != null");
-
- if (expression.NodeType != ExpressionType.Convert &&
- expression.NodeType != ExpressionType.ConvertChecked &&
- expression.NodeType != ExpressionType.TypeAs)
- {
- return false;
- }
-
- return expression.Type.IsAssignableFrom(expression.Operand.Type);
- }
-
- internal static bool MatchParameterMemberAccess(Expression expression)
- {
- Debug.Assert(expression != null, "lambda != null");
-
- LambdaExpression lambda = StripTo<LambdaExpression>(expression);
- if (lambda == null || lambda.Parameters.Count != 1)
- {
- return false;
- }
-
- ParameterExpression parameter = lambda.Parameters[0];
- Expression body = StripCastMethodCalls(lambda.Body);
- MemberExpression memberAccess = StripTo<MemberExpression>(body);
- while (memberAccess != null)
- {
- if (memberAccess.Expression == parameter)
- {
- return true;
- }
-
- memberAccess = StripTo<MemberExpression>(memberAccess.Expression);
- }
-
- return false;
- }
-
- internal static bool MatchPropertyAccess(Expression e, out MemberExpression member, out Expression instance, out List<string> propertyPath)
- {
- instance = null;
- propertyPath = null;
-
- MemberExpression me = StripTo<MemberExpression>(e);
- member = me;
- while (me != null)
- {
- PropertyInfo pi;
- if (MatchNonPrivateReadableProperty(me, out pi))
- {
- if (propertyPath == null)
- {
- propertyPath = new List<string>();
- }
-
- propertyPath.Insert(0, pi.Name);
- e = me.Expression;
- me = StripTo<MemberExpression>(e);
- }
- else
- {
- me = null;
- }
- }
-
- if (propertyPath != null)
- {
- instance = e;
- return true;
- }
-
- return false;
- }
-
- internal static bool MatchConstant(Expression e, out ConstantExpression constExpr)
- {
- constExpr = e as ConstantExpression;
- return constExpr != null;
- }
-
- internal static bool MatchAnd(Expression e)
- {
- BinaryExpression be = e as BinaryExpression;
- return (be != null && (be.NodeType == ExpressionType.And || be.NodeType == ExpressionType.AndAlso));
- }
-
- internal static bool MatchNonPrivateReadableProperty(Expression e, out PropertyInfo propInfo)
- {
- MemberExpression me = e as MemberExpression;
- if (me == null)
- {
- propInfo = null;
- return false;
- }
-
- return MatchNonPrivateReadableProperty(me, out propInfo);
- }
-
- internal static bool MatchNonPrivateReadableProperty(MemberExpression me, out PropertyInfo propInfo)
- {
- Debug.Assert(me != null, "me != null");
-
- propInfo = null;
-
- if (me.Member.MemberType == MemberTypes.Property)
- {
- PropertyInfo pi = (PropertyInfo)me.Member;
- if (pi.CanRead && !TypeSystem.IsPrivate(pi))
- {
- propInfo = pi;
- return true;
- }
- }
-
- return false;
- }
-
- internal static bool MatchKeyProperty(Expression expression, out PropertyInfo property)
- {
- property = null;
-
- PropertyInfo pi;
- if (!PatternRules.MatchNonPrivateReadableProperty(expression, out pi))
- {
- return false;
- }
-
- if (GetKeyProperties(pi.ReflectedType).Contains(pi, PropertyInfoEqualityComparer.Instance))
- {
- property = pi;
- return true;
- }
-
- return false;
- }
-
- internal static List<PropertyInfo> GetKeyProperties(Type type)
- {
- Debug.Assert(type != null, "type != null");
- ClientType clientType = ClientType.Create(type, false );
- var result = new List<…
Large files files are truncated, but you can click here to view the full file