PageRenderTime 55ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/System.Data.Services.Client/Client/System/Data/Services/Client/ALinq/ResourceBinder.cs

https://bitbucket.org/danipen/mono
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
  1. //Copyright 2010 Microsoft Corporation
  2. //
  3. //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
  4. //You may obtain a copy of the License at
  5. //
  6. //http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. //Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
  9. //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. //See the License for the specific language governing permissions and limitations under the License.
  11. namespace System.Data.Services.Client
  12. {
  13. #region Namespaces.
  14. using System;
  15. using System.Collections.Generic;
  16. using System.Collections.ObjectModel;
  17. using System.Diagnostics;
  18. using System.Linq;
  19. using System.Linq.Expressions;
  20. using System.Reflection;
  21. #endregion Namespaces.
  22. internal class ResourceBinder : DataServiceExpressionVisitor
  23. {
  24. internal static Expression Bind(Expression e)
  25. {
  26. Debug.Assert(e != null, "e != null");
  27. ResourceBinder rb = new ResourceBinder();
  28. Expression boundExpression = rb.Visit(e);
  29. VerifyKeyPredicates(boundExpression);
  30. VerifyNotSelectManyProjection(boundExpression);
  31. return boundExpression;
  32. }
  33. internal static bool IsMissingKeyPredicates(Expression expression)
  34. {
  35. ResourceExpression re = expression as ResourceExpression;
  36. if (re != null)
  37. {
  38. if (IsMissingKeyPredicates(re.Source))
  39. {
  40. return true;
  41. }
  42. if (re.Source != null)
  43. {
  44. ResourceSetExpression rse = re.Source as ResourceSetExpression;
  45. if ((rse != null) && !rse.HasKeyPredicate)
  46. {
  47. return true;
  48. }
  49. }
  50. }
  51. return false;
  52. }
  53. internal static void VerifyKeyPredicates(Expression e)
  54. {
  55. if (IsMissingKeyPredicates(e))
  56. {
  57. throw new NotSupportedException(Strings.ALinq_CantNavigateWithoutKeyPredicate);
  58. }
  59. }
  60. internal static void VerifyNotSelectManyProjection(Expression expression)
  61. {
  62. Debug.Assert(expression != null, "expression != null");
  63. ResourceSetExpression resourceSet = expression as ResourceSetExpression;
  64. if (resourceSet != null)
  65. {
  66. ProjectionQueryOptionExpression projection = resourceSet.Projection;
  67. if (projection != null)
  68. {
  69. Debug.Assert(projection.Selector != null, "projection.Selector != null -- otherwise incorrectly constructed");
  70. MethodCallExpression call = StripTo<MethodCallExpression>(projection.Selector.Body);
  71. if (call != null && call.Method.Name == "SelectMany")
  72. {
  73. throw new NotSupportedException(Strings.ALinq_UnsupportedExpression(call));
  74. }
  75. }
  76. else if (resourceSet.HasTransparentScope)
  77. {
  78. throw new NotSupportedException(Strings.ALinq_UnsupportedExpression(resourceSet));
  79. }
  80. }
  81. }
  82. private static Expression AnalyzePredicate(MethodCallExpression mce)
  83. {
  84. Debug.Assert(mce != null, "mce != null -- caller couldn't have know the expression kind otherwise");
  85. Debug.Assert(mce.Method.Name == "Where", "mce.Method.Name == 'Where' -- otherwise this isn't a predicate");
  86. ResourceSetExpression input;
  87. LambdaExpression le;
  88. if (!TryGetResourceSetMethodArguments(mce, out input, out le))
  89. {
  90. ValidationRules.RequireNonSingleton(mce.Arguments[0]);
  91. return mce;
  92. }
  93. List<Expression> conjuncts = new List<Expression>();
  94. AddConjuncts(le.Body, conjuncts);
  95. Dictionary<ResourceSetExpression, List<Expression>> predicatesByTarget = new Dictionary<ResourceSetExpression, List<Expression>>(ReferenceEqualityComparer<ResourceSetExpression>.Instance);
  96. List<ResourceExpression> referencedInputs = new List<ResourceExpression>();
  97. foreach (Expression e in conjuncts)
  98. {
  99. Expression reboundPredicate = InputBinder.Bind(e, input, le.Parameters[0], referencedInputs);
  100. if (referencedInputs.Count > 1)
  101. {
  102. return mce;
  103. }
  104. ResourceSetExpression boundTarget = (referencedInputs.Count == 0 ? input : referencedInputs[0] as ResourceSetExpression);
  105. if (boundTarget == null)
  106. {
  107. return mce;
  108. }
  109. List<Expression> targetPredicates = null;
  110. if (!predicatesByTarget.TryGetValue(boundTarget, out targetPredicates))
  111. {
  112. targetPredicates = new List<Expression>();
  113. predicatesByTarget[boundTarget] = targetPredicates;
  114. }
  115. targetPredicates.Add(reboundPredicate);
  116. referencedInputs.Clear();
  117. }
  118. conjuncts = null;
  119. List<Expression> inputPredicates;
  120. if (predicatesByTarget.TryGetValue(input, out inputPredicates))
  121. {
  122. predicatesByTarget.Remove(input);
  123. }
  124. else
  125. {
  126. inputPredicates = null;
  127. }
  128. foreach (KeyValuePair<ResourceSetExpression, List<Expression>> predicates in predicatesByTarget)
  129. {
  130. ResourceSetExpression target = predicates.Key;
  131. List<Expression> clauses = predicates.Value;
  132. Dictionary<PropertyInfo, ConstantExpression> keyValues;
  133. if (!ExtractKeyPredicate(target, clauses, out keyValues) ||
  134. clauses.Count > 0)
  135. {
  136. return mce;
  137. }
  138. Debug.Assert(!target.HasQueryOptions, "Navigation source had query options?");
  139. SetKeyPredicate(target, keyValues);
  140. }
  141. if (inputPredicates != null)
  142. {
  143. Dictionary<PropertyInfo, ConstantExpression> inputKeyValues;
  144. if (ExtractKeyPredicate(input, inputPredicates, out inputKeyValues))
  145. {
  146. if (input.HasSequenceQueryOptions)
  147. {
  148. Expression predicateFilter = BuildKeyPredicateFilter(input.CreateReference(), inputKeyValues);
  149. inputPredicates.Add(predicateFilter);
  150. }
  151. else
  152. {
  153. SetKeyPredicate(input, inputKeyValues);
  154. }
  155. }
  156. if (inputPredicates.Count > 0)
  157. {
  158. if (input.KeyPredicate != null)
  159. {
  160. Expression predicateFilter = BuildKeyPredicateFilter(input.CreateReference(), input.KeyPredicate);
  161. inputPredicates.Add(predicateFilter);
  162. input.KeyPredicate = null;
  163. }
  164. int start;
  165. Expression newFilter;
  166. if (input.Filter != null)
  167. {
  168. start = 0;
  169. newFilter = input.Filter.Predicate;
  170. }
  171. else
  172. {
  173. start = 1;
  174. newFilter = inputPredicates[0];
  175. }
  176. for (int idx = start; idx < inputPredicates.Count; idx++)
  177. {
  178. newFilter = Expression.And(newFilter, inputPredicates[idx]);
  179. }
  180. AddSequenceQueryOption(input, new FilterQueryOptionExpression(mce.Method.ReturnType, newFilter));
  181. }
  182. }
  183. return input; }
  184. private static void SetKeyPredicate(ResourceSetExpression rse, Dictionary<PropertyInfo, ConstantExpression> keyValues)
  185. {
  186. Debug.Assert(rse != null, "rse != null");
  187. Debug.Assert(keyValues != null, "keyValues != null");
  188. if (rse.KeyPredicate == null)
  189. {
  190. rse.KeyPredicate = new Dictionary<PropertyInfo, ConstantExpression>(EqualityComparer<PropertyInfo>.Default);
  191. }
  192. foreach(var g in keyValues)
  193. {
  194. if (rse.KeyPredicate.Keys.Contains(g.Key))
  195. {
  196. throw Error.NotSupported(Strings.ALinq_CanOnlyApplyOneKeyPredicate);
  197. }
  198. rse.KeyPredicate.Add(g.Key, g.Value);
  199. }
  200. }
  201. private static bool CollectionContentsEqual<T>(ICollection<T> left, ICollection<T> right, IEqualityComparer<T> comparer) where T : class
  202. {
  203. Debug.Assert(left != null, "left != null");
  204. Debug.Assert(right != null, "right != null");
  205. Debug.Assert(comparer != null, "comparer != null");
  206. if (left.Count != right.Count)
  207. {
  208. return false;
  209. }
  210. if (left.Count == 1)
  211. {
  212. return comparer.Equals(left.First(), right.First());
  213. }
  214. else
  215. {
  216. #if ASTORIA_LIGHT
  217. HashSet<T> leftElements = new HashSet<T>(comparer);
  218. foreach (var l in left)
  219. {
  220. leftElements.Add(l);
  221. }
  222. #else
  223. HashSet<T> leftElements = new HashSet<T>(left, comparer);
  224. #endif
  225. foreach (T rightElement in right)
  226. {
  227. if (!leftElements.Contains(rightElement))
  228. {
  229. return false;
  230. }
  231. }
  232. return true;
  233. }
  234. }
  235. private static bool ExtractKeyPredicate(
  236. ResourceSetExpression target,
  237. List<Expression> predicates,
  238. out Dictionary<PropertyInfo, ConstantExpression> keyValues)
  239. {
  240. Debug.Assert(target != null, "target != null");
  241. Debug.Assert(predicates != null, "predicates != null");
  242. keyValues = null;
  243. List<Expression> nonKeyPredicates = null;
  244. foreach (Expression predicate in predicates)
  245. {
  246. PropertyInfo property;
  247. ConstantExpression constantValue;
  248. if (PatternRules.MatchKeyComparison(predicate, out property, out constantValue))
  249. {
  250. if (keyValues == null)
  251. {
  252. keyValues = new Dictionary<PropertyInfo, ConstantExpression>(EqualityComparer<PropertyInfo>.Default);
  253. }
  254. else if (keyValues.ContainsKey(property))
  255. {
  256. throw Error.NotSupported(Strings.ALinq_CanOnlyApplyOneKeyPredicate);
  257. }
  258. keyValues.Add(property, constantValue);
  259. }
  260. else
  261. {
  262. if (nonKeyPredicates == null)
  263. {
  264. nonKeyPredicates = new List<Expression>();
  265. }
  266. nonKeyPredicates.Add(predicate);
  267. }
  268. }
  269. Debug.Assert(keyValues != null || nonKeyPredicates != null, "No key predicates or non-key predicates found?");
  270. if (keyValues != null)
  271. {
  272. var properties = PatternRules.GetKeyProperties(target.CreateReference().Type);
  273. if (!CollectionContentsEqual(properties, keyValues.Keys, PropertyInfoEqualityComparer.Instance))
  274. {
  275. keyValues = null;
  276. return false;
  277. }
  278. }
  279. if (keyValues != null)
  280. {
  281. predicates.Clear();
  282. if (nonKeyPredicates != null)
  283. {
  284. predicates.AddRange(nonKeyPredicates);
  285. }
  286. }
  287. return keyValues != null;
  288. }
  289. private static Expression BuildKeyPredicateFilter(InputReferenceExpression input, Dictionary<PropertyInfo, ConstantExpression> keyValuesDictionary)
  290. {
  291. Debug.Assert(input != null, "input != null");
  292. Debug.Assert(keyValuesDictionary != null, "keyValuesDictionary != null");
  293. Debug.Assert(keyValuesDictionary.Count > 0, "At least one key property is required in a key predicate");
  294. Expression retExpr = null;
  295. foreach (KeyValuePair<PropertyInfo, ConstantExpression> keyValue in keyValuesDictionary)
  296. {
  297. Expression clause = Expression.Equal(Expression.Property(input, keyValue.Key), keyValue.Value);
  298. if (retExpr == null)
  299. {
  300. retExpr = clause;
  301. }
  302. else
  303. {
  304. retExpr = Expression.And(retExpr, clause);
  305. }
  306. }
  307. return retExpr;
  308. }
  309. private static void AddConjuncts(Expression e, List<Expression> conjuncts)
  310. {
  311. Debug.Assert(conjuncts != null, "conjuncts != null");
  312. if (PatternRules.MatchAnd(e))
  313. {
  314. BinaryExpression be = (BinaryExpression)e;
  315. AddConjuncts(be.Left, conjuncts);
  316. AddConjuncts(be.Right, conjuncts);
  317. }
  318. else
  319. {
  320. conjuncts.Add(e);
  321. }
  322. }
  323. internal bool AnalyzeProjection(MethodCallExpression mce, SequenceMethod sequenceMethod, out Expression e)
  324. {
  325. Debug.Assert(mce != null, "mce != null");
  326. Debug.Assert(
  327. sequenceMethod == SequenceMethod.Select || sequenceMethod == SequenceMethod.SelectManyResultSelector,
  328. "sequenceMethod == SequenceMethod.Select(ManyResultSelector)");
  329. e = mce;
  330. bool matchMembers = sequenceMethod == SequenceMethod.SelectManyResultSelector;
  331. ResourceExpression source = this.Visit(mce.Arguments[0]) as ResourceExpression;
  332. if (source == null)
  333. {
  334. return false;
  335. }
  336. if (sequenceMethod == SequenceMethod.SelectManyResultSelector)
  337. {
  338. Expression collectionSelector = mce.Arguments[1];
  339. if (!PatternRules.MatchParameterMemberAccess(collectionSelector))
  340. {
  341. return false;
  342. }
  343. Expression resultSelector = mce.Arguments[2];
  344. LambdaExpression resultLambda;
  345. if (!PatternRules.MatchDoubleArgumentLambda(resultSelector, out resultLambda))
  346. {
  347. return false;
  348. }
  349. if (ExpressionPresenceVisitor.IsExpressionPresent(resultLambda.Parameters[0], resultLambda.Body))
  350. {
  351. return false;
  352. }
  353. List<ResourceExpression> referencedExpressions = new List<ResourceExpression>();
  354. LambdaExpression collectionLambda = StripTo<LambdaExpression>(collectionSelector);
  355. Expression collectorReference = InputBinder.Bind(collectionLambda.Body, source, collectionLambda.Parameters[0], referencedExpressions);
  356. collectorReference = StripCastMethodCalls(collectorReference);
  357. MemberExpression navigationMember;
  358. if (!PatternRules.MatchPropertyProjectionSet(source, collectorReference, out navigationMember))
  359. {
  360. return false;
  361. }
  362. collectorReference = navigationMember;
  363. ResourceExpression resultSelectorSource = CreateResourceSetExpression(mce.Method.ReturnType, source, collectorReference, TypeSystem.GetElementType(collectorReference.Type));
  364. if (!PatternRules.MatchMemberInitExpressionWithDefaultConstructor(resultSelectorSource, resultLambda) &&
  365. !PatternRules.MatchNewExpression(resultSelectorSource, resultLambda))
  366. {
  367. return false;
  368. }
  369. #if ASTORIA_LIGHT
  370. resultLambda = ExpressionHelpers.CreateLambda(resultLambda.Body, new ParameterExpression[] { resultLambda.Parameters[1] });
  371. #else
  372. resultLambda = Expression.Lambda(resultLambda.Body, new ParameterExpression[] { resultLambda.Parameters[1] });
  373. #endif
  374. ResourceExpression resultWithProjection = resultSelectorSource.CreateCloneWithNewType(mce.Type);
  375. bool isProjection;
  376. try
  377. {
  378. isProjection = ProjectionAnalyzer.Analyze(resultLambda, resultWithProjection, false);
  379. }
  380. catch (NotSupportedException)
  381. {
  382. isProjection = false;
  383. }
  384. if (!isProjection)
  385. {
  386. return false;
  387. }
  388. e = resultWithProjection;
  389. ValidationRules.RequireCanProject(resultSelectorSource);
  390. }
  391. else
  392. {
  393. LambdaExpression lambda;
  394. if (!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out lambda))
  395. {
  396. return false;
  397. }
  398. lambda = ProjectionRewriter.TryToRewrite(lambda, source.ResourceType);
  399. ResourceExpression re = source.CreateCloneWithNewType(mce.Type);
  400. if (!ProjectionAnalyzer.Analyze(lambda, re, matchMembers))
  401. {
  402. return false;
  403. }
  404. ValidationRules.RequireCanProject(source);
  405. e = re;
  406. }
  407. return true;
  408. }
  409. internal static Expression AnalyzeNavigation(MethodCallExpression mce)
  410. {
  411. Debug.Assert(mce != null, "mce != null");
  412. Expression input = mce.Arguments[0];
  413. LambdaExpression le;
  414. ResourceExpression navSource;
  415. Expression boundProjection;
  416. MemberExpression navigationMember;
  417. if (!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out le))
  418. {
  419. return mce;
  420. }
  421. else if (PatternRules.MatchIdentitySelector(le))
  422. {
  423. return input;
  424. }
  425. else if (PatternRules.MatchTransparentIdentitySelector(input, le))
  426. {
  427. return RemoveTransparentScope(mce.Method.ReturnType, (ResourceSetExpression)input);
  428. }
  429. else if (IsValidNavigationSource(input, out navSource) &&
  430. TryBindToInput(navSource, le, out boundProjection) &&
  431. PatternRules.MatchPropertyProjectionSingleton(navSource, boundProjection, out navigationMember))
  432. {
  433. boundProjection = navigationMember;
  434. return CreateNavigationPropertySingletonExpression(mce.Method.ReturnType, navSource, boundProjection);
  435. }
  436. return mce;
  437. }
  438. private static bool IsValidNavigationSource(Expression input, out ResourceExpression sourceExpression)
  439. {
  440. ValidationRules.RequireCanNavigate(input);
  441. sourceExpression = input as ResourceExpression;
  442. return sourceExpression != null;
  443. }
  444. internal static Expression AnalyzeSelectMany(MethodCallExpression mce)
  445. {
  446. Debug.Assert(mce != null, "mce != null");
  447. if (mce.Arguments.Count != 2 && mce.Arguments.Count != 3)
  448. {
  449. return mce;
  450. }
  451. ResourceExpression input;
  452. if (!IsValidNavigationSource(mce.Arguments[0], out input))
  453. {
  454. return mce;
  455. }
  456. LambdaExpression collectorSelector;
  457. if (!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out collectorSelector))
  458. {
  459. return mce;
  460. }
  461. List<ResourceExpression> referencedInputs = new List<ResourceExpression>();
  462. Expression navPropRef = InputBinder.Bind(collectorSelector.Body, input, collectorSelector.Parameters[0], referencedInputs);
  463. Type resourceType = TypeSystem.GetElementType(navPropRef.Type);
  464. navPropRef = StripCastMethodCalls(navPropRef);
  465. ResourceSetExpression rse = null;
  466. MemberExpression navigationMember;
  467. if (PatternRules.MatchPropertyProjectionSet(input, navPropRef, out navigationMember))
  468. {
  469. navPropRef = navigationMember;
  470. rse = CreateResourceSetExpression(mce.Method.ReturnType, input, navPropRef, resourceType);
  471. }
  472. else
  473. {
  474. return mce;
  475. }
  476. if (mce.Arguments.Count == 3)
  477. {
  478. return AnalyzeSelectManySelector(mce, rse);
  479. }
  480. else
  481. {
  482. return rse;
  483. }
  484. }
  485. private static Expression AnalyzeSelectManySelector(MethodCallExpression selectManyCall, ResourceSetExpression sourceResourceSet)
  486. {
  487. Debug.Assert(selectManyCall != null, "selectManyCall != null");
  488. LambdaExpression selector = StripTo<LambdaExpression>(selectManyCall.Arguments[2]);
  489. Expression result;
  490. ResourceSetExpression.TransparentAccessors transparentScope;
  491. if (PatternRules.MatchTransparentScopeSelector(sourceResourceSet, selector, out transparentScope))
  492. {
  493. sourceResourceSet.TransparentScope = transparentScope;
  494. result = sourceResourceSet;
  495. }
  496. else if (PatternRules.MatchIdentityProjectionResultSelector(selector))
  497. {
  498. result = sourceResourceSet;
  499. }
  500. else if (PatternRules.MatchMemberInitExpressionWithDefaultConstructor(sourceResourceSet, selector) || PatternRules.MatchNewExpression(sourceResourceSet, selector))
  501. {
  502. #if ASTORIA_LIGHT
  503. selector = ExpressionHelpers.CreateLambda(selector.Body, new ParameterExpression[] { selector.Parameters[1] });
  504. #else
  505. selector = Expression.Lambda(selector.Body, new ParameterExpression[] { selector.Parameters[1] });
  506. #endif
  507. if (!ProjectionAnalyzer.Analyze(selector, sourceResourceSet, false))
  508. {
  509. result = selectManyCall;
  510. }
  511. else
  512. {
  513. result = sourceResourceSet;
  514. }
  515. }
  516. else
  517. {
  518. result = selectManyCall;
  519. }
  520. return result;
  521. }
  522. internal static Expression ApplyOrdering(MethodCallExpression mce, ResourceSetExpression input, Expression selector, bool descending, bool thenBy)
  523. {
  524. List<OrderByQueryOptionExpression.Selector> selectors;
  525. if (!thenBy)
  526. {
  527. selectors = new List<OrderByQueryOptionExpression.Selector>();
  528. AddSequenceQueryOption(input, new OrderByQueryOptionExpression(mce.Type, selectors));
  529. }
  530. else
  531. {
  532. Debug.Assert(input.OrderBy != null, "input.OrderBy != null");
  533. selectors = input.OrderBy.Selectors;
  534. }
  535. selectors.Add(new OrderByQueryOptionExpression.Selector(selector, descending));
  536. return input;
  537. }
  538. #if !ASTORIA_LIGHT
  539. private static Expression LimitCardinality(MethodCallExpression mce, int maxCardinality)
  540. {
  541. Debug.Assert(mce != null, "mce != null");
  542. Debug.Assert(maxCardinality > 0, "Cardinality must be at least 1");
  543. if (mce.Arguments.Count != 1)
  544. {
  545. return mce;
  546. }
  547. ResourceSetExpression rse = mce.Arguments[0] as ResourceSetExpression;
  548. if (rse != null)
  549. {
  550. if (!rse.HasKeyPredicate && (ResourceExpressionType)rse.NodeType != ResourceExpressionType.ResourceNavigationProperty)
  551. {
  552. if (rse.Take == null || (int)rse.Take.TakeAmount.Value > maxCardinality)
  553. {
  554. AddSequenceQueryOption(rse, new TakeQueryOptionExpression(mce.Type, Expression.Constant(maxCardinality)));
  555. }
  556. }
  557. return mce.Arguments[0];
  558. }
  559. else if (mce.Arguments[0] is NavigationPropertySingletonExpression)
  560. {
  561. return mce.Arguments[0];
  562. }
  563. return mce;
  564. }
  565. #endif
  566. private static Expression AnalyzeCast(MethodCallExpression mce)
  567. {
  568. ResourceExpression re = mce.Arguments[0] as ResourceExpression;
  569. if (re != null)
  570. {
  571. return re.CreateCloneWithNewType(mce.Method.ReturnType);
  572. }
  573. return mce;
  574. }
  575. private static Expression StripConvert(Expression e)
  576. {
  577. UnaryExpression ue = e as UnaryExpression;
  578. if (ue != null &&
  579. ue.NodeType == ExpressionType.Convert &&
  580. ue.Type.IsGenericType &&
  581. (ue.Type.GetGenericTypeDefinition() == typeof(DataServiceQuery<>) ||
  582. ue.Type.GetGenericTypeDefinition() == typeof(DataServiceQuery<>.DataServiceOrderedQuery)))
  583. {
  584. e = ue.Operand;
  585. ResourceExpression re = e as ResourceExpression;
  586. if (re != null)
  587. {
  588. e = re.CreateCloneWithNewType(ue.Type);
  589. }
  590. }
  591. return e;
  592. }
  593. private static Expression AnalyzeExpand(MethodCallExpression mce)
  594. {
  595. Expression obj = StripConvert(mce.Object);
  596. ResourceExpression re = obj as ResourceExpression;
  597. if (re == null)
  598. {
  599. return mce;
  600. }
  601. ValidationRules.RequireCanExpand(re);
  602. ConstantExpression ce = StripTo<ConstantExpression>(mce.Arguments[0]);
  603. string path = (string)ce.Value;
  604. if (!re.ExpandPaths.Contains(path))
  605. {
  606. re.ExpandPaths.Add(path);
  607. }
  608. return re;
  609. }
  610. private static Expression AnalyzeAddCustomQueryOption(MethodCallExpression mce)
  611. {
  612. Expression obj = StripConvert(mce.Object);
  613. ResourceExpression re = obj as ResourceExpression;
  614. if (re == null)
  615. {
  616. return mce;
  617. }
  618. ValidationRules.RequireCanAddCustomQueryOption(re);
  619. ConstantExpression name = StripTo<ConstantExpression>(mce.Arguments[0]);
  620. ConstantExpression value = StripTo<ConstantExpression>(mce.Arguments[1]);
  621. if (((string)name.Value).Trim() == UriHelper.DOLLARSIGN + UriHelper.OPTIONEXPAND)
  622. {
  623. ValidationRules.RequireCanExpand(re);
  624. re.ExpandPaths = re.ExpandPaths.Union(new string[] { (string) value.Value }, StringComparer.Ordinal).ToList();
  625. }
  626. else
  627. {
  628. ValidationRules.RequireLegalCustomQueryOption(mce.Arguments[0], re);
  629. re.CustomQueryOptions.Add(name, value);
  630. }
  631. return re;
  632. }
  633. private static Expression AnalyzeAddCountOption(MethodCallExpression mce, CountOption countOption)
  634. {
  635. Expression obj = StripConvert(mce.Object);
  636. ResourceExpression re = obj as ResourceExpression;
  637. if (re == null)
  638. {
  639. return mce;
  640. }
  641. ValidationRules.RequireCanAddCount(re);
  642. ValidationRules.RequireNonSingleton(re);
  643. re.CountOption = countOption;
  644. return re;
  645. }
  646. private static ResourceSetExpression CreateResourceSetExpression(Type type, ResourceExpression source, Expression memberExpression, Type resourceType)
  647. {
  648. Debug.Assert(type != null, "type != null");
  649. Debug.Assert(source != null, "source != null");
  650. Debug.Assert(memberExpression != null, "memberExpression != null");
  651. Debug.Assert(resourceType != null, "resourceType != null");
  652. Type elementType = TypeSystem.GetElementType(type);
  653. Debug.Assert(elementType != null, "elementType != null -- otherwise the set isn't going to act like a collection");
  654. Type expressionType = typeof(IOrderedQueryable<>).MakeGenericType(elementType);
  655. ResourceSetExpression newResource = new ResourceSetExpression(expressionType, source, memberExpression, resourceType, source.ExpandPaths.ToList(), source.CountOption, source.CustomQueryOptions.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), null);
  656. source.ExpandPaths.Clear();
  657. source.CountOption = CountOption.None;
  658. source.CustomQueryOptions.Clear();
  659. return newResource;
  660. }
  661. private static NavigationPropertySingletonExpression CreateNavigationPropertySingletonExpression(Type type, ResourceExpression source, Expression memberExpression)
  662. {
  663. NavigationPropertySingletonExpression newResource = new NavigationPropertySingletonExpression(type, source, memberExpression, memberExpression.Type, source.ExpandPaths.ToList(), source.CountOption, source.CustomQueryOptions.ToDictionary(kvp => kvp.Key, kvp => kvp.Value), null);
  664. source.ExpandPaths.Clear();
  665. source.CountOption = CountOption.None;
  666. source.CustomQueryOptions.Clear();
  667. return newResource;
  668. }
  669. private static ResourceSetExpression RemoveTransparentScope(Type expectedResultType, ResourceSetExpression input)
  670. {
  671. ResourceSetExpression newResource = new ResourceSetExpression(expectedResultType, input.Source, input.MemberExpression, input.ResourceType, input.ExpandPaths, input.CountOption, input.CustomQueryOptions, input.Projection);
  672. newResource.KeyPredicate = input.KeyPredicate;
  673. foreach (QueryOptionExpression queryOption in input.SequenceQueryOptions)
  674. {
  675. newResource.AddSequenceQueryOption(queryOption);
  676. }
  677. newResource.OverrideInputReference(input);
  678. return newResource;
  679. }
  680. internal static Expression StripConvertToAssignable(Expression e)
  681. {
  682. Debug.Assert(e != null, "e != null");
  683. Expression result;
  684. UnaryExpression unary = e as UnaryExpression;
  685. if (unary != null && PatternRules.MatchConvertToAssignable(unary))
  686. {
  687. result = unary.Operand;
  688. }
  689. else
  690. {
  691. result = e;
  692. }
  693. return result;
  694. }
  695. internal static T StripTo<T>(Expression expression) where T : Expression
  696. {
  697. Debug.Assert(expression != null, "expression != null");
  698. Expression result;
  699. do
  700. {
  701. result = expression;
  702. expression = expression.NodeType == ExpressionType.Quote ? ((UnaryExpression)expression).Operand : expression;
  703. expression = StripConvertToAssignable(expression);
  704. }
  705. while (result != expression);
  706. return result as T;
  707. }
  708. internal override Expression VisitResourceSetExpression(ResourceSetExpression rse)
  709. {
  710. Debug.Assert(rse != null, "rse != null");
  711. if ((ResourceExpressionType)rse.NodeType == ResourceExpressionType.RootResourceSet)
  712. {
  713. return new ResourceSetExpression(rse.Type, rse.Source, rse.MemberExpression, rse.ResourceType, null, CountOption.None, null, null);
  714. }
  715. return rse;
  716. }
  717. private static bool TryGetResourceSetMethodArguments(MethodCallExpression mce, out ResourceSetExpression input, out LambdaExpression lambda)
  718. {
  719. input = null;
  720. lambda = null;
  721. input = mce.Arguments[0] as ResourceSetExpression;
  722. if (input != null &&
  723. PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out lambda))
  724. {
  725. return true;
  726. }
  727. return false;
  728. }
  729. private static bool TryBindToInput(ResourceExpression input, LambdaExpression le, out Expression bound)
  730. {
  731. List<ResourceExpression> referencedInputs = new List<ResourceExpression>();
  732. bound = InputBinder.Bind(le.Body, input, le.Parameters[0], referencedInputs);
  733. if (referencedInputs.Count > 1 || (referencedInputs.Count == 1 && referencedInputs[0] != input))
  734. {
  735. bound = null;
  736. }
  737. return bound != null;
  738. }
  739. private static Expression AnalyzeResourceSetMethod(MethodCallExpression mce, Func<MethodCallExpression, ResourceSetExpression, Expression, Expression> sequenceMethodAnalyzer)
  740. {
  741. ResourceSetExpression input;
  742. LambdaExpression le;
  743. if (!TryGetResourceSetMethodArguments(mce, out input, out le))
  744. {
  745. return mce;
  746. }
  747. Expression lambdaBody;
  748. if (!TryBindToInput(input, le, out lambdaBody))
  749. {
  750. return mce;
  751. }
  752. return sequenceMethodAnalyzer(mce, input, lambdaBody);
  753. }
  754. private static Expression AnalyzeResourceSetConstantMethod(MethodCallExpression mce, Func<MethodCallExpression, ResourceExpression, ConstantExpression, Expression> constantMethodAnalyzer)
  755. {
  756. ResourceExpression input = (ResourceExpression)mce.Arguments[0];
  757. ConstantExpression constantArg = StripTo<ConstantExpression>(mce.Arguments[1]);
  758. if (null == constantArg)
  759. {
  760. return mce;
  761. }
  762. return constantMethodAnalyzer(mce, input, constantArg);
  763. }
  764. private static Expression AnalyzeCountMethod(MethodCallExpression mce)
  765. {
  766. ResourceExpression re = (ResourceExpression)mce.Arguments[0];
  767. if (re == null)
  768. {
  769. return mce;
  770. }
  771. ValidationRules.RequireCanAddCount(re);
  772. ValidationRules.RequireNonSingleton(re);
  773. re.CountOption = CountOption.ValueOnly;
  774. return re;
  775. }
  776. private static void AddSequenceQueryOption(ResourceExpression target, QueryOptionExpression qoe)
  777. {
  778. ValidationRules.RequireNonSingleton(target);
  779. ResourceSetExpression rse = (ResourceSetExpression)target;
  780. switch (qoe.NodeType)
  781. {
  782. case (ExpressionType)ResourceExpressionType.FilterQueryOption:
  783. if (rse.Skip != null)
  784. {
  785. throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "skip"));
  786. }
  787. else if (rse.Take != null)
  788. {
  789. throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "top"));
  790. }
  791. else if (rse.Projection != null)
  792. {
  793. throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "select"));
  794. }
  795. break;
  796. case (ExpressionType)ResourceExpressionType.OrderByQueryOption:
  797. if (rse.Skip != null)
  798. {
  799. throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "skip"));
  800. }
  801. else if (rse.Take != null)
  802. {
  803. throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "top"));
  804. }
  805. else if (rse.Projection != null)
  806. {
  807. throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "select"));
  808. }
  809. break;
  810. case (ExpressionType)ResourceExpressionType.SkipQueryOption:
  811. if (rse.Take != null)
  812. {
  813. throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("skip", "top"));
  814. }
  815. break;
  816. default:
  817. break;
  818. }
  819. rse.AddSequenceQueryOption(qoe);
  820. }
  821. internal override Expression VisitBinary(BinaryExpression b)
  822. {
  823. Expression e = base.VisitBinary(b);
  824. if (PatternRules.MatchStringAddition(e))
  825. {
  826. BinaryExpression be = StripTo<BinaryExpression>(e);
  827. MethodInfo mi = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string)});
  828. return Expression.Call(mi, new Expression[] {be.Left, be.Right});
  829. }
  830. return e;
  831. }
  832. internal override Expression VisitMemberAccess(MemberExpression m)
  833. {
  834. Expression e = base.VisitMemberAccess(m);
  835. MemberExpression me = StripTo<MemberExpression>(e);
  836. PropertyInfo pi;
  837. MethodInfo mi;
  838. if (me != null &&
  839. PatternRules.MatchNonPrivateReadableProperty(me, out pi) &&
  840. TypeSystem.TryGetPropertyAsMethod(pi, out mi))
  841. {
  842. return Expression.Call(me.Expression, mi);
  843. }
  844. return e;
  845. }
  846. internal override Expression VisitMethodCall(MethodCallExpression mce)
  847. {
  848. Expression e;
  849. SequenceMethod sequenceMethod;
  850. if (ReflectionUtil.TryIdentifySequenceMethod(mce.Method, out sequenceMethod))
  851. {
  852. if (sequenceMethod == SequenceMethod.Select ||
  853. sequenceMethod == SequenceMethod.SelectManyResultSelector)
  854. {
  855. if (this.AnalyzeProjection(mce, sequenceMethod, out e))
  856. {
  857. return e;
  858. }
  859. }
  860. }
  861. e = base.VisitMethodCall(mce);
  862. mce = e as MethodCallExpression;
  863. if (mce != null)
  864. {
  865. if (ReflectionUtil.TryIdentifySequenceMethod(mce.Method, out sequenceMethod))
  866. {
  867. switch (sequenceMethod)
  868. {
  869. case SequenceMethod.Where:
  870. return AnalyzePredicate(mce);
  871. case SequenceMethod.Select:
  872. return AnalyzeNavigation(mce);
  873. case SequenceMethod.SelectMany:
  874. case SequenceMethod.SelectManyResultSelector:
  875. {
  876. Expression result = AnalyzeSelectMany(mce);
  877. return result;
  878. }
  879. case SequenceMethod.Take:
  880. return AnalyzeResourceSetConstantMethod(mce, (callExp, resource, takeCount) => { AddSequenceQueryOption(resource, new TakeQueryOptionExpression(callExp.Type, takeCount)); return resource; });
  881. case SequenceMethod.Skip:
  882. return AnalyzeResourceSetConstantMethod(mce, (callExp, resource, skipCount) => { AddSequenceQueryOption(resource, new SkipQueryOptionExpression(callExp.Type, skipCount)); return resource; });
  883. case SequenceMethod.OrderBy:
  884. return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector,false, false));
  885. case SequenceMethod.ThenBy:
  886. return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, false,true));
  887. case SequenceMethod.OrderByDescending:
  888. return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, true, false));
  889. case SequenceMethod.ThenByDescending:
  890. return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector,true, true));
  891. #if !ASTORIA_LIGHT
  892. case SequenceMethod.First:
  893. case SequenceMethod.FirstOrDefault:
  894. return LimitCardinality(mce, 1);
  895. case SequenceMethod.Single:
  896. case SequenceMethod.SingleOrDefault:
  897. return LimitCardinality(mce, 2);
  898. #endif
  899. case SequenceMethod.Cast:
  900. return AnalyzeCast(mce);
  901. case SequenceMethod.LongCount:
  902. case SequenceMethod.Count:
  903. return AnalyzeCountMethod(mce);
  904. default:
  905. throw Error.MethodNotSupported(mce);
  906. }
  907. }
  908. else if (mce.Method.DeclaringType.IsGenericType &&
  909. mce.Method.DeclaringType.GetGenericTypeDefinition() == typeof(DataServiceQuery<>))
  910. {
  911. Type t = typeof(DataServiceQuery<>).MakeGenericType(mce.Method.DeclaringType.GetGenericArguments()[0]);
  912. if (mce.Method == t.GetMethod("Expand", new Type[] { typeof(string) }))
  913. {
  914. return AnalyzeExpand(mce);
  915. }
  916. else if (mce.Method == t.GetMethod("AddQueryOption", new Type[] { typeof(string), typeof(object) }))
  917. {
  918. return AnalyzeAddCustomQueryOption(mce);
  919. }
  920. else if (mce.Method == t.GetMethod("IncludeTotalCount"))
  921. {
  922. return AnalyzeAddCountOption(mce, CountOption.InlineAll);
  923. }
  924. else
  925. {
  926. throw Error.MethodNotSupported(mce);
  927. }
  928. }
  929. return mce;
  930. }
  931. return e;
  932. }
  933. private static Expression StripCastMethodCalls(Expression expression)
  934. {
  935. Debug.Assert(expression != null, "expression != null");
  936. MethodCallExpression call = StripTo<MethodCallExpression>(expression);
  937. while (call != null && ReflectionUtil.IsSequenceMethod(call.Method, SequenceMethod.Cast))
  938. {
  939. expression = call.Arguments[0];
  940. call = StripTo<MethodCallExpression>(expression);
  941. }
  942. return expression;
  943. }
  944. internal static class PatternRules
  945. {
  946. internal static bool MatchConvertToAssignable(UnaryExpression expression)
  947. {
  948. Debug.Assert(expression != null, "expression != null");
  949. if (expression.NodeType != ExpressionType.Convert &&
  950. expression.NodeType != ExpressionType.ConvertChecked &&
  951. expression.NodeType != ExpressionType.TypeAs)
  952. {
  953. return false;
  954. }
  955. return expression.Type.IsAssignableFrom(expression.Operand.Type);
  956. }
  957. internal static bool MatchParameterMemberAccess(Expression expression)
  958. {
  959. Debug.Assert(expression != null, "lambda != null");
  960. LambdaExpression lambda = StripTo<LambdaExpression>(expression);
  961. if (lambda == null || lambda.Parameters.Count != 1)
  962. {
  963. return false;
  964. }
  965. ParameterExpression parameter = lambda.Parameters[0];
  966. Expression body = StripCastMethodCalls(lambda.Body);
  967. MemberExpression memberAccess = StripTo<MemberExpression>(body);
  968. while (memberAccess != null)
  969. {
  970. if (memberAccess.Expression == parameter)
  971. {
  972. return true;
  973. }
  974. memberAccess = StripTo<MemberExpression>(memberAccess.Expression);
  975. }
  976. return false;
  977. }
  978. internal static bool MatchPropertyAccess(Expression e, out MemberExpression member, out Expression instance, out List<string> propertyPath)
  979. {
  980. instance = null;
  981. propertyPath = null;
  982. MemberExpression me = StripTo<MemberExpression>(e);
  983. member = me;
  984. while (me != null)
  985. {
  986. PropertyInfo pi;
  987. if (MatchNonPrivateReadableProperty(me, out pi))
  988. {
  989. if (propertyPath == null)
  990. {
  991. propertyPath = new List<string>();
  992. }
  993. propertyPath.Insert(0, pi.Name);
  994. e = me.Expression;
  995. me = StripTo<MemberExpression>(e);
  996. }
  997. else
  998. {
  999. me = null;
  1000. }
  1001. }
  1002. if (propertyPath != null)
  1003. {
  1004. instance = e;
  1005. return true;
  1006. }
  1007. return false;
  1008. }
  1009. internal static bool MatchConstant(Expression e, out ConstantExpression constExpr)
  1010. {
  1011. constExpr = e as ConstantExpression;
  1012. return constExpr != null;
  1013. }
  1014. internal static bool MatchAnd(Expression e)
  1015. {
  1016. BinaryExpression be = e as BinaryExpression;
  1017. return (be != null && (be.NodeType == ExpressionType.And || be.NodeType == ExpressionType.AndAlso));
  1018. }
  1019. internal static bool MatchNonPrivateReadableProperty(Expression e, out PropertyInfo propInfo)
  1020. {
  1021. MemberExpression me = e as MemberExpression;
  1022. if (me == null)
  1023. {
  1024. propInfo = null;
  1025. return false;
  1026. }
  1027. return MatchNonPrivateReadableProperty(me, out propInfo);
  1028. }
  1029. internal static bool MatchNonPrivateReadableProperty(MemberExpression me, out PropertyInfo propInfo)
  1030. {
  1031. Debug.Assert(me != null, "me != null");
  1032. propInfo = null;
  1033. if (me.Member.MemberType == MemberTypes.Property)
  1034. {
  1035. PropertyInfo pi = (PropertyInfo)me.Member;
  1036. if (pi.CanRead && !TypeSystem.IsPrivate(pi))
  1037. {
  1038. propInfo = pi;
  1039. return true;
  1040. }
  1041. }
  1042. return false;
  1043. }
  1044. internal static bool MatchKeyProperty(Expression expression, out PropertyInfo property)
  1045. {
  1046. property = null;
  1047. PropertyInfo pi;
  1048. if (!PatternRules.MatchNonPrivateReadableProperty(expression, out pi))
  1049. {
  1050. return false;
  1051. }
  1052. if (GetKeyProperties(pi.ReflectedType).Contains(pi, PropertyInfoEqualityComparer.Instance))
  1053. {
  1054. property = pi;
  1055. return true;
  1056. }
  1057. return false;
  1058. }
  1059. internal static List<PropertyInfo> GetKeyProperties(Type type)
  1060. {
  1061. Debug.Assert(type != null, "type != null");
  1062. ClientType clientType = ClientType.Create(type, false );
  1063. var result = new List<PropertyInfo>();
  1064. foreach (var property in clientType.Properties)
  1065. {
  1066. if (property.KeyProperty)
  1067. {
  1068. result.Add(property.DeclaringType.GetProperty(property.PropertyName));
  1069. }
  1070. }
  1071. return result;
  1072. }
  1073. internal static bool MatchKeyComparison(Expression e, out PropertyInfo keyProperty, out ConstantExpression keyValue)
  1074. {
  1075. if (PatternRules.MatchBinaryEquality(e))
  1076. {
  1077. BinaryExpression be = (BinaryExpression)e;
  1078. if ((PatternRules.MatchKeyProperty(be.Left, out keyProperty) && PatternRules.MatchConstant(be.Right, out keyValue)) ||
  1079. (PatternRules.MatchKeyProperty(be.Right, out keyProperty) && PatternRules.MatchConstant(be.Left, out keyValue)))
  1080. {
  1081. return keyValue.Value != null;
  1082. }
  1083. }
  1084. keyProperty = null;
  1085. keyValue = null;
  1086. return false;
  1087. }
  1088. internal static bool MatchReferenceEquals(Expression expression)
  1089. {
  1090. Debug.Assert(expression != null, "expression != null");
  1091. MethodCallExpression call = expression as MethodCallExpression;
  1092. if (call == null)
  1093. {
  1094. return false;
  1095. }
  1096. return call.Method == typeof(object).GetMethod("ReferenceEquals");
  1097. }
  1098. internal static bool MatchResource(Expression expression, out ResourceExpression resource)
  1099. {
  1100. resource = expression as ResourceExpression;
  1101. return resource != null;
  1102. }
  1103. internal static bool MatchDoubleArgumentLambda(Expression expression, out LambdaExpression lambda)
  1104. {
  1105. return MatchNaryLambda(expression, 2, out lambda);
  1106. }
  1107. internal static bool MatchIdentitySelector(LambdaExpression lambda)
  1108. {
  1109. Debug.Assert(lambda != null, "lambda != null");
  1110. ParameterExpression parameter = lambda.Parameters[0];
  1111. return parameter == StripTo<ParameterExpression>(lambda.Body);
  1112. }
  1113. internal static bool MatchSingleArgumentLambda(Expression expression, out LambdaExpression lambda)
  1114. {
  1115. return MatchNaryLambda(expression, 1, out lambda);
  1116. }
  1117. internal static bool MatchTransparentIdentitySelector(Expression input, LambdaExpression selector)
  1118. {
  1119. if (selector.Parameters.Count != 1)
  1120. {
  1121. return false;
  1122. }
  1123. ResourceSetExpression rse = input as ResourceSetExpression;
  1124. if (rse == null || rse.TransparentScope == null)
  1125. {
  1126. return false;
  1127. }
  1128. Expression potentialRef = selector.Body;
  1129. ParameterExpression expectedTarget = selector.Parameters[0];
  1130. MemberExpression propertyMember;
  1131. Expression paramRef;
  1132. List<string> refPath;
  1133. if (!MatchPropertyAccess(potentialRef, out propertyMember, out paramRef, out refPath))
  1134. {
  1135. return false;
  1136. }
  1137. Debug.Assert(refPath != null, "refPath != null -- otherwise MatchPropertyAccess should not have returned true");
  1138. return paramRef == expectedTarget && refPath.Count == 1 && refPath[0] == rse.TransparentScope.Accessor;
  1139. }
  1140. internal static bool MatchIdentityProjectionResultSelector(Expression e)
  1141. {
  1142. LambdaExpression le = (LambdaExpression)e;
  1143. return (le.Body == le.Parameters[1]);
  1144. }
  1145. internal static bool MatchTransparentScopeSelector(ResourceSetExpression input, LambdaExpression resultSelector, out ResourceSetExpression.TransparentAccessors transparentScope)
  1146. {
  1147. transparentScope = null;
  1148. if (resultSelector.Body.NodeType != ExpressionType.New)
  1149. {
  1150. return false;
  1151. }
  1152. NewExpression ne = (NewExpression)resultSelector.Body;
  1153. if (ne.Arguments.Count < 2)
  1154. {
  1155. return false;
  1156. }
  1157. if (ne.Type.BaseType != typeof(object))
  1158. {
  1159. return false;
  1160. }
  1161. ParameterInfo[] constructorParams = ne.Constructor.GetParameters();
  1162. if (ne.Members.Count != constructorParams.Length)
  1163. {
  1164. return false;
  1165. }
  1166. ResourceSetExpression inputSourceSet = input.Source as ResourceSetExpression;
  1167. int introducedMemberIndex = -1;
  1168. ParameterExpression collectorSourceParameter = resultSelector.Parameters[0];
  1169. ParameterExpression introducedRangeParameter = resultSelector.Parameters[1];
  1170. MemberInfo[] memberProperties = new MemberInfo[ne.Members.Count];
  1171. PropertyInfo[] properties = ne.Type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
  1172. Dictionary<string, Expression> sourceAccessors = new Dictionary<string, Expression>(constructorParams.Length - 1, StringComparer.Ordinal);
  1173. for (int i = 0; i < ne.Arguments.Count; i++)
  1174. {
  1175. Expression argument = ne.Arguments[i];
  1176. MemberInfo member = ne.Members[i];
  1177. if (!ExpressionIsSimpleAccess(argument, resultSelector.Parameters))
  1178. {
  1179. return false;
  1180. }
  1181. if (member.MemberType == MemberTypes.Method)
  1182. {
  1183. member = properties.Where(property => property.GetGetMethod() == member).FirstOrDefault();
  1184. if (member == null)
  1185. {
  1186. return false;
  1187. }
  1188. }
  1189. if (member.Name != constructorParams[i].Name)
  1190. {
  1191. return false;
  1192. }
  1193. memberProperties[i] = member;
  1194. ParameterExpression argumentAsParameter = StripTo<ParameterExpression>(argument);
  1195. if (introducedRangeParameter == argumentAsParameter)
  1196. {
  1197. if (introducedMemberIndex != -1)
  1198. {
  1199. return false;
  1200. }
  1201. introducedMemberIndex = i;
  1202. }
  1203. else if (collectorSourceParameter == argumentAsParameter)
  1204. {
  1205. sourceAccessors[member.Name] = inputSourceSet.CreateReference();
  1206. }
  1207. else
  1208. {
  1209. List<ResourceExpression> referencedInputs = new List<ResourceExpression>();
  1210. Expression boundArgument = InputBinder.Bind(argument, inputSourceSet, resultSelector.Parameters[0], referencedInputs);
  1211. if (referencedInputs.Count != 1)
  1212. {
  1213. return false;
  1214. }
  1215. sourceAccessors[member.Name] = referencedInputs[0].CreateReference();
  1216. }
  1217. }
  1218. if (introducedMemberIndex == -1)
  1219. {
  1220. return false;
  1221. }
  1222. string resultAccessor = memberProperties[introducedMemberIndex].Name;
  1223. transparentScope = new ResourceSetExpression.TransparentAccessors(resultAccessor, sourceAccessors);
  1224. return true;
  1225. }
  1226. internal static bool MatchPropertyProjectionSet(ResourceExpression input, Expression potentialPropertyRef, out MemberExpression navigationMember)
  1227. {
  1228. return MatchNavigationPropertyProjection(input, potentialPropertyRef, true, out navigationMember);
  1229. }
  1230. internal static bool MatchPropertyProjectionSingleton(ResourceExpression input, Expression potentialPropertyRef, out MemberExpression navigationMember)
  1231. {
  1232. return MatchNavigationPropertyProjection(input, potentialPropertyRef, false, out navigationMember);
  1233. }
  1234. private static bool MatchNavigationPropertyProjection(ResourceExpression input, Expression potentialPropertyRef, bool requireSet, out MemberExpression navigationMember)
  1235. {
  1236. if (PatternRules.MatchNonSingletonProperty(potentialPropertyRef) == requireSet)
  1237. {
  1238. Expression foundInstance;
  1239. List<string> propertyNames;
  1240. if (MatchPropertyAccess(potentialPropertyRef, out navigationMember, out foundInstance, out propertyNames))
  1241. {
  1242. if (foundInstance == input.CreateReference())
  1243. {
  1244. return true;
  1245. }
  1246. }
  1247. }
  1248. navigationMember = null;
  1249. return false;
  1250. }
  1251. internal static bool MatchMemberInitExpressionWithDefaultConstructor(Expression source, LambdaExpression e)
  1252. {
  1253. MemberInitExpression mie = StripTo<MemberInitExpression>(e.Body);
  1254. ResourceExpression resource;
  1255. return MatchResource(source, out resource) && (mie != null) && (mie.NewExpression.Arguments.Count == 0);
  1256. }
  1257. internal static bool MatchNewExpression(Expression source, LambdaExpression e)
  1258. {
  1259. ResourceExpression resource;
  1260. return MatchResource(source, out resource) && (e.Body is NewExpression);
  1261. }
  1262. internal static bool MatchNot(Expression expression)
  1263. {
  1264. Debug.Assert(expression != null, "expression != null");
  1265. return expression.NodeType == ExpressionType.Not;
  1266. }
  1267. internal static bool MatchNonSingletonProperty(Expression e)
  1268. {
  1269. return (TypeSystem.FindIEnumerable(e.Type) != null) &&
  1270. e.Type != typeof(char[]) &&
  1271. e.Type != typeof(byte[]);
  1272. }
  1273. internal static MatchNullCheckResult MatchNullCheck(Expression entityInScope, ConditionalExpression conditional)
  1274. {
  1275. Debug.Assert(conditional != null, "conditional != null");
  1276. MatchNullCheckResult result = new MatchNullCheckResult();
  1277. MatchEqualityCheckResult equalityCheck = MatchEquality(conditional.Test);
  1278. if (!equalityCheck.Match)
  1279. {
  1280. return result;
  1281. }
  1282. Expression assignedCandidate;
  1283. if (equalityCheck.EqualityYieldsTrue)
  1284. {
  1285. if (!MatchNullConstant(conditional.IfTrue))
  1286. {
  1287. return result;
  1288. }
  1289. assignedCandidate = conditional.IfFalse;
  1290. }
  1291. else
  1292. {
  1293. if (!MatchNullConstant(conditional.IfFalse))
  1294. {
  1295. return result;
  1296. }
  1297. assignedCandidate = conditional.IfTrue;
  1298. }
  1299. Expression memberCandidate;
  1300. if (MatchNullConstant(equalityCheck.TestLeft))
  1301. {
  1302. memberCandidate = equalityCheck.TestRight;
  1303. }
  1304. else if (MatchNullConstant(equalityCheck.TestRight))
  1305. {
  1306. memberCandidate = equalityCheck.TestLeft;
  1307. }
  1308. else
  1309. {
  1310. return result;
  1311. }
  1312. Debug.Assert(assignedCandidate != null, "assignedCandidate != null");
  1313. Debug.Assert(memberCandidate != null, "memberCandidate != null");
  1314. MemberAssignmentAnalysis assignedAnalysis = MemberAssignmentAnalysis.Analyze(entityInScope, assignedCandidate);
  1315. if (assignedAnalysis.MultiplePathsFound)
  1316. {
  1317. return result;
  1318. }
  1319. MemberAssignmentAnalysis memberAnalysis = MemberAssignmentAnalysis.Analyze(entityInScope, memberCandidate);
  1320. if (memberAnalysis.MultiplePathsFound)
  1321. {
  1322. return result;
  1323. }
  1324. Expression[] assignedExpressions = assignedAnalysis.GetExpressionsToTargetEntity();
  1325. Expression[] memberExpressions = memberAnalysis.GetExpressionsToTargetEntity();
  1326. if (memberExpressions.Length > assignedExpressions.Length)
  1327. {
  1328. return result;
  1329. }
  1330. for (int i = 0; i < memberExpressions.Length; i++)
  1331. {
  1332. Expression assigned = assignedExpressions[i];
  1333. Expression member = memberExpressions[i];
  1334. if (assigned == member)
  1335. {
  1336. continue;
  1337. }
  1338. if (assigned.NodeType != member.NodeType || assigned.NodeType != ExpressionType.MemberAccess)
  1339. {
  1340. return result;
  1341. }
  1342. if (((MemberExpression)assigned).Member != ((MemberExpression)member).Member)
  1343. {
  1344. return result;
  1345. }
  1346. }
  1347. result.AssignExpression = assignedCandidate;
  1348. result.Match = true;
  1349. result.TestToNullExpression = memberCandidate;
  1350. return result;
  1351. }
  1352. internal static bool MatchNullConstant(Expression expression)
  1353. {
  1354. Debug.Assert(expression != null, "expression != null");
  1355. ConstantExpression constant = expression as ConstantExpression;
  1356. if (constant != null && constant.Value == null)
  1357. {
  1358. return true;
  1359. }
  1360. return false;
  1361. }
  1362. internal static bool MatchBinaryExpression(Expression e)
  1363. {
  1364. return (e is BinaryExpression);
  1365. }
  1366. internal static bool MatchBinaryEquality(Expression e)
  1367. {
  1368. return (PatternRules.MatchBinaryExpression(e) && ((BinaryExpression)e).NodeType == ExpressionType.Equal);
  1369. }
  1370. internal static bool MatchStringAddition(Expression e)
  1371. {
  1372. if (e.NodeType == ExpressionType.Add)
  1373. {
  1374. BinaryExpression be = e as BinaryExpression;
  1375. return be != null &&
  1376. be.Left.Type == typeof(string) &&
  1377. be.Right.Type == typeof(string);
  1378. }
  1379. return false;
  1380. }
  1381. internal static bool MatchNewDataServiceCollectionOfT(NewExpression nex)
  1382. {
  1383. return nex.Type.IsGenericType && WebUtil.IsDataServiceCollectionType(nex.Type.GetGenericTypeDefinition());
  1384. }
  1385. internal static MatchEqualityCheckResult MatchEquality(Expression expression)
  1386. {
  1387. Debug.Assert(expression != null, "expression != null");
  1388. MatchEqualityCheckResult result = new MatchEqualityCheckResult();
  1389. result.Match = false;
  1390. result.EqualityYieldsTrue = true;
  1391. while (true)
  1392. {
  1393. if (MatchReferenceEquals(expression))
  1394. {
  1395. MethodCallExpression call = (MethodCallExpression)expression;
  1396. result.Match = true;
  1397. result.TestLeft = call.Arguments[0];
  1398. result.TestRight = call.Arguments[1];
  1399. break;
  1400. }
  1401. else if (MatchNot(expression))
  1402. {
  1403. result.EqualityYieldsTrue = !result.EqualityYieldsTrue;
  1404. expression = ((UnaryExpression)expression).Operand;
  1405. }
  1406. else
  1407. {
  1408. BinaryExpression test = expression as BinaryExpression;
  1409. if (test == null)
  1410. {
  1411. break;
  1412. }
  1413. if (test.NodeType == ExpressionType.NotEqual)
  1414. {
  1415. result.EqualityYieldsTrue = !result.EqualityYieldsTrue;
  1416. }
  1417. else if (test.NodeType != ExpressionType.Equal)
  1418. {
  1419. break;
  1420. }
  1421. result.TestLeft = test.Left;
  1422. result.TestRight = test.Right;
  1423. result.Match = true;
  1424. break;
  1425. }
  1426. }
  1427. return result;
  1428. }
  1429. private static bool ExpressionIsSimpleAccess(Expression argument, ReadOnlyCollection<ParameterExpression> expressions)
  1430. {
  1431. Debug.Assert(argument != null, "argument != null");
  1432. Debug.Assert(expressions != null, "expressions != null");
  1433. Expression source = argument;
  1434. MemberExpression member;
  1435. do
  1436. {
  1437. member = source as MemberExpression;
  1438. if (member != null)
  1439. {
  1440. source = member.Expression;
  1441. }
  1442. }
  1443. while (member != null);
  1444. ParameterExpression parameter = source as ParameterExpression;
  1445. if (parameter == null)
  1446. {
  1447. return false;
  1448. }
  1449. return expressions.Contains(parameter);
  1450. }
  1451. private static bool MatchNaryLambda(Expression expression, int parameterCount, out LambdaExpression lambda)
  1452. {
  1453. lambda = null;
  1454. LambdaExpression le = StripTo<LambdaExpression>(expression);
  1455. if (le != null && le.Parameters.Count == parameterCount)
  1456. {
  1457. lambda = le;
  1458. }
  1459. return lambda != null;
  1460. }
  1461. internal struct MatchNullCheckResult
  1462. {
  1463. internal Expression AssignExpression;
  1464. internal bool Match;
  1465. internal Expression TestToNullExpression;
  1466. }
  1467. internal struct MatchEqualityCheckResult
  1468. {
  1469. internal bool EqualityYieldsTrue;
  1470. internal bool Match;
  1471. internal Expression TestLeft;
  1472. internal Expression TestRight;
  1473. }
  1474. }
  1475. private static class ValidationRules
  1476. {
  1477. internal static void RequireCanNavigate(Expression e)
  1478. {
  1479. ResourceSetExpression resourceSet = e as ResourceSetExpression;
  1480. if (resourceSet != null && resourceSet.HasSequenceQueryOptions)
  1481. {
  1482. throw new NotSupportedException(Strings.ALinq_QueryOptionsOnlyAllowedOnLeafNodes);
  1483. }
  1484. ResourceExpression resource;
  1485. if (PatternRules.MatchResource(e, out resource) && resource.Projection != null)
  1486. {
  1487. throw new NotSupportedException(Strings.ALinq_ProjectionOnlyAllowedOnLeafNodes);
  1488. }
  1489. }
  1490. internal static void RequireCanProject(Expression e)
  1491. {
  1492. ResourceExpression re = (ResourceExpression)e;
  1493. if (!PatternRules.MatchResource(e, out re))
  1494. {
  1495. throw new NotSupportedException(Strings.ALinq_CanOnlyProjectTheLeaf);
  1496. }
  1497. if (re.Projection != null)
  1498. {
  1499. throw new NotSupportedException(Strings.ALinq_ProjectionCanOnlyHaveOneProjection);
  1500. }
  1501. if (re.ExpandPaths.Count > 0)
  1502. {
  1503. throw new NotSupportedException(Strings.ALinq_CannotProjectWithExplicitExpansion);
  1504. }
  1505. }
  1506. internal static void RequireCanExpand(Expression e)
  1507. {
  1508. ResourceExpression re = (ResourceExpression)e;
  1509. if (!PatternRules.MatchResource(e, out re))
  1510. {
  1511. throw new NotSupportedException(Strings.ALinq_CantExpand);
  1512. }
  1513. if (re.Projection != null)
  1514. {
  1515. throw new NotSupportedException(Strings.ALinq_CannotProjectWithExplicitExpansion);
  1516. }
  1517. }
  1518. internal static void RequireCanAddCount(Expression e)
  1519. {
  1520. ResourceExpression re = (ResourceExpression)e;
  1521. if (!PatternRules.MatchResource(e, out re))
  1522. {
  1523. throw new NotSupportedException(Strings.ALinq_CannotAddCountOption);
  1524. }
  1525. if (re.CountOption != CountOption.None)
  1526. {
  1527. throw new NotSupportedException(Strings.ALinq_CannotAddCountOptionConflict);
  1528. }
  1529. }
  1530. internal static void RequireCanAddCustomQueryOption(Expression e)
  1531. {
  1532. ResourceExpression re = (ResourceExpression)e;
  1533. if (!PatternRules.MatchResource(e, out re))
  1534. {
  1535. throw new NotSupportedException(Strings.ALinq_CantAddQueryOption);
  1536. }
  1537. }
  1538. internal static void RequireNonSingleton(Expression e)
  1539. {
  1540. ResourceExpression re = e as ResourceExpression;
  1541. if (re != null && re.IsSingleton)
  1542. {
  1543. throw new NotSupportedException(Strings.ALinq_QueryOptionsOnlyAllowedOnSingletons);
  1544. }
  1545. }
  1546. internal static void RequireLegalCustomQueryOption(Expression e, ResourceExpression target)
  1547. {
  1548. string name = ((string)(e as ConstantExpression).Value).Trim();
  1549. if (name[0] == UriHelper.DOLLARSIGN)
  1550. {
  1551. if (target.CustomQueryOptions.Any(c => (string)c.Key.Value == name))
  1552. {
  1553. throw new NotSupportedException(Strings.ALinq_CantAddDuplicateQueryOption(name));
  1554. }
  1555. ResourceSetExpression rse = target as ResourceSetExpression;
  1556. if (rse != null)
  1557. {
  1558. switch (name.Substring(1))
  1559. {
  1560. case UriHelper.OPTIONFILTER:
  1561. if (rse.Filter != null)
  1562. {
  1563. throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
  1564. }
  1565. break;
  1566. case UriHelper.OPTIONORDERBY:
  1567. if (rse.OrderBy != null)
  1568. throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
  1569. break;
  1570. case UriHelper.OPTIONEXPAND:
  1571. break;
  1572. case UriHelper.OPTIONSKIP:
  1573. if (rse.Skip != null)
  1574. throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
  1575. break;
  1576. case UriHelper.OPTIONTOP:
  1577. if (rse.Take != null)
  1578. throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
  1579. break;
  1580. case UriHelper.OPTIONCOUNT:
  1581. if (rse.CountOption != CountOption.None)
  1582. throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
  1583. break;
  1584. default:
  1585. throw new NotSupportedException(Strings.ALinq_CantAddQueryOptionStartingWithDollarSign(name));
  1586. }
  1587. }
  1588. }
  1589. }
  1590. }
  1591. private sealed class PropertyInfoEqualityComparer : IEqualityComparer<PropertyInfo>
  1592. {
  1593. private PropertyInfoEqualityComparer() { }
  1594. internal static readonly PropertyInfoEqualityComparer Instance = new PropertyInfoEqualityComparer();
  1595. #region IEqualityComparer<TypeUsage> Members
  1596. public bool Equals(PropertyInfo left, PropertyInfo right)
  1597. {
  1598. if (object.ReferenceEquals(left, right)) { return true; }
  1599. if (null == left || null == right) { return false; }
  1600. return object.ReferenceEquals(left.DeclaringType, right.DeclaringType) && left.Name.Equals(right.Name);
  1601. }
  1602. public int GetHashCode(PropertyInfo obj)
  1603. {
  1604. Debug.Assert(obj != null, "obj != null");
  1605. return obj.GetHashCode();
  1606. }
  1607. #endregion
  1608. }
  1609. private sealed class ExpressionPresenceVisitor : DataServiceExpressionVisitor
  1610. {
  1611. #region Private fields.
  1612. private readonly Expression target;
  1613. private bool found;
  1614. #endregion Private fields.
  1615. private ExpressionPresenceVisitor(Expression target)
  1616. {
  1617. Debug.Assert(target != null, "target != null");
  1618. this.target = target;
  1619. }
  1620. internal static bool IsExpressionPresent(Expression target, Expression tree)
  1621. {
  1622. Debug.Assert(target != null, "target != null");
  1623. Debug.Assert(tree != null, "tree != null");
  1624. ExpressionPresenceVisitor visitor = new ExpressionPresenceVisitor(target);
  1625. visitor.Visit(tree);
  1626. return visitor.found;
  1627. }
  1628. internal override Expression Visit(Expression exp)
  1629. {
  1630. Expression result;
  1631. if (this.found || object.ReferenceEquals(this.target, exp))
  1632. {
  1633. this.found = true;
  1634. result = exp;
  1635. }
  1636. else
  1637. {
  1638. result = base.Visit(exp);
  1639. }
  1640. return result;
  1641. }
  1642. }
  1643. }
  1644. }