PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/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

Large files files are truncated, but you can click here to view the full file

  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<

Large files files are truncated, but you can click here to view the full file